create-fhevm-example 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contracts/advanced/BlindAuction.sol +255 -0
- package/contracts/advanced/EncryptedEscrow.sol +315 -0
- package/contracts/advanced/HiddenVoting.sol +231 -0
- package/contracts/advanced/PrivateKYC.sol +309 -0
- package/contracts/advanced/PrivatePayroll.sol +285 -0
- package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +160 -0
- package/contracts/basic/decryption/PublicDecryptSingleValue.sol +142 -0
- package/contracts/basic/decryption/UserDecryptMultipleValues.sol +61 -0
- package/contracts/basic/decryption/UserDecryptSingleValue.sol +59 -0
- package/contracts/basic/encryption/EncryptMultipleValues.sol +72 -0
- package/contracts/basic/encryption/EncryptSingleValue.sol +44 -0
- package/contracts/basic/encryption/FHECounter.sol +54 -0
- package/contracts/basic/fhe-operations/FHEAdd.sol +51 -0
- package/contracts/basic/fhe-operations/FHEArithmetic.sol +99 -0
- package/contracts/basic/fhe-operations/FHEComparison.sol +116 -0
- package/contracts/basic/fhe-operations/FHEIfThenElse.sol +53 -0
- package/contracts/concepts/FHEAccessControl.sol +94 -0
- package/contracts/concepts/FHEAntiPatterns.sol +329 -0
- package/contracts/concepts/FHEHandles.sol +128 -0
- package/contracts/concepts/FHEInputProof.sol +104 -0
- package/contracts/gaming/EncryptedLottery.sol +298 -0
- package/contracts/gaming/EncryptedPoker.sol +337 -0
- package/contracts/gaming/RockPaperScissors.sol +213 -0
- package/contracts/openzeppelin/ERC7984.sol +85 -0
- package/contracts/openzeppelin/ERC7984ERC20Wrapper.sol +43 -0
- package/contracts/openzeppelin/SwapERC7984ToERC20.sol +110 -0
- package/contracts/openzeppelin/SwapERC7984ToERC7984.sol +48 -0
- package/contracts/openzeppelin/VestingWallet.sol +147 -0
- package/contracts/openzeppelin/mocks/ERC20Mock.sol +31 -0
- package/dist/scripts/commands/add-mode.d.ts.map +1 -0
- package/dist/scripts/{add-mode.js → commands/add-mode.js} +27 -61
- package/dist/scripts/commands/doctor.d.ts.map +1 -0
- package/dist/scripts/{doctor.js → commands/doctor.js} +2 -2
- package/dist/scripts/commands/generate-config.d.ts.map +1 -0
- package/dist/scripts/{generate-config.js → commands/generate-config.js} +3 -10
- package/dist/scripts/commands/generate-docs.d.ts.map +1 -0
- package/dist/scripts/{generate-docs.js → commands/generate-docs.js} +4 -3
- package/dist/scripts/commands/maintenance.d.ts.map +1 -0
- package/dist/scripts/{maintenance.js → commands/maintenance.js} +11 -10
- package/dist/scripts/index.js +14 -33
- package/dist/scripts/{builders.d.ts → shared/builders.d.ts} +2 -2
- package/dist/scripts/shared/builders.d.ts.map +1 -0
- package/dist/scripts/{builders.js → shared/builders.js} +49 -30
- package/dist/scripts/{config.d.ts → shared/config.d.ts} +0 -2
- package/dist/scripts/shared/config.d.ts.map +1 -0
- package/dist/scripts/{config.js → shared/config.js} +48 -59
- package/dist/scripts/shared/generators.d.ts +42 -0
- package/dist/scripts/shared/generators.d.ts.map +1 -0
- package/dist/scripts/{utils.js → shared/generators.js} +34 -271
- package/dist/scripts/shared/ui.d.ts.map +1 -0
- package/dist/scripts/{ui.js → shared/ui.js} +3 -2
- package/dist/scripts/{utils.d.ts → shared/utils.d.ts} +4 -27
- package/dist/scripts/shared/utils.d.ts.map +1 -0
- package/dist/scripts/shared/utils.js +228 -0
- package/fhevm-hardhat-template/.eslintignore +26 -0
- package/fhevm-hardhat-template/.eslintrc.yml +21 -0
- package/fhevm-hardhat-template/.github/workflows/main.yml +47 -0
- package/fhevm-hardhat-template/.github/workflows/manual-windows.yml +28 -0
- package/fhevm-hardhat-template/.github/workflows/manual.yml +28 -0
- package/fhevm-hardhat-template/.prettierignore +25 -0
- package/fhevm-hardhat-template/.prettierrc.yml +15 -0
- package/fhevm-hardhat-template/.solcover.js +4 -0
- package/fhevm-hardhat-template/.solhint.json +12 -0
- package/fhevm-hardhat-template/.solhintignore +3 -0
- package/fhevm-hardhat-template/.vscode/extensions.json +3 -0
- package/fhevm-hardhat-template/.vscode/settings.json +9 -0
- package/fhevm-hardhat-template/LICENSE +33 -0
- package/fhevm-hardhat-template/README.md +110 -0
- package/fhevm-hardhat-template/contracts/FHECounter.sol +46 -0
- package/fhevm-hardhat-template/deploy/deploy.ts +17 -0
- package/fhevm-hardhat-template/hardhat.config.ts +90 -0
- package/fhevm-hardhat-template/package-lock.json +10405 -0
- package/fhevm-hardhat-template/package.json +104 -0
- package/fhevm-hardhat-template/tasks/FHECounter.ts +184 -0
- package/fhevm-hardhat-template/tasks/accounts.ts +9 -0
- package/fhevm-hardhat-template/test/FHECounter.ts +104 -0
- package/fhevm-hardhat-template/test/FHECounterSepolia.ts +104 -0
- package/fhevm-hardhat-template/tsconfig.json +23 -0
- package/package.json +11 -8
- package/test/advanced/BlindAuction.ts +246 -0
- package/test/advanced/EncryptedEscrow.ts +295 -0
- package/test/advanced/HiddenVoting.ts +268 -0
- package/test/advanced/PrivateKYC.ts +382 -0
- package/test/advanced/PrivatePayroll.ts +253 -0
- package/test/basic/decryption/PublicDecryptMultipleValues.ts +254 -0
- package/test/basic/decryption/PublicDecryptSingleValue.ts +264 -0
- package/test/basic/decryption/UserDecryptMultipleValues.ts +107 -0
- package/test/basic/decryption/UserDecryptSingleValue.ts +97 -0
- package/test/basic/encryption/EncryptMultipleValues.ts +110 -0
- package/test/basic/encryption/EncryptSingleValue.ts +124 -0
- package/test/basic/encryption/FHECounter.ts +112 -0
- package/test/basic/fhe-operations/FHEAdd.ts +97 -0
- package/test/basic/fhe-operations/FHEArithmetic.ts +161 -0
- package/test/basic/fhe-operations/FHEComparison.ts +167 -0
- package/test/basic/fhe-operations/FHEIfThenElse.ts +97 -0
- package/test/concepts/FHEAccessControl.ts +154 -0
- package/test/concepts/FHEAntiPatterns.ts +111 -0
- package/test/concepts/FHEHandles.ts +156 -0
- package/test/concepts/FHEInputProof.ts +151 -0
- package/test/gaming/EncryptedLottery.ts +214 -0
- package/test/gaming/EncryptedPoker.ts +349 -0
- package/test/gaming/RockPaperScissors.ts +205 -0
- package/test/openzeppelin/ERC7984.ts +142 -0
- package/test/openzeppelin/ERC7984ERC20Wrapper.ts +71 -0
- package/test/openzeppelin/SwapERC7984ToERC20.ts +76 -0
- package/test/openzeppelin/SwapERC7984ToERC7984.ts +113 -0
- package/test/openzeppelin/VestingWallet.ts +89 -0
- package/dist/scripts/add-mode.d.ts.map +0 -1
- package/dist/scripts/builders.d.ts.map +0 -1
- package/dist/scripts/config.d.ts.map +0 -1
- package/dist/scripts/doctor.d.ts.map +0 -1
- package/dist/scripts/generate-config.d.ts.map +0 -1
- package/dist/scripts/generate-docs.d.ts.map +0 -1
- package/dist/scripts/help.d.ts +0 -9
- package/dist/scripts/help.d.ts.map +0 -1
- package/dist/scripts/help.js +0 -73
- package/dist/scripts/maintenance.d.ts.map +0 -1
- package/dist/scripts/ui.d.ts.map +0 -1
- package/dist/scripts/utils.d.ts.map +0 -1
- /package/dist/scripts/{add-mode.d.ts → commands/add-mode.d.ts} +0 -0
- /package/dist/scripts/{doctor.d.ts → commands/doctor.d.ts} +0 -0
- /package/dist/scripts/{generate-config.d.ts → commands/generate-config.d.ts} +0 -0
- /package/dist/scripts/{generate-docs.d.ts → commands/generate-docs.d.ts} +0 -0
- /package/dist/scripts/{maintenance.d.ts → commands/maintenance.d.ts} +0 -0
- /package/dist/scripts/{ui.d.ts → shared/ui.d.ts} +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FhevmType,
|
|
3
|
+
HardhatFhevmRuntimeEnvironment,
|
|
4
|
+
} from "@fhevm/hardhat-plugin";
|
|
5
|
+
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
|
|
6
|
+
import { expect } from "chai";
|
|
7
|
+
import { ethers } from "hardhat";
|
|
8
|
+
import * as hre from "hardhat";
|
|
9
|
+
|
|
10
|
+
import { FHEAccessControl, FHEAccessControl__factory } from "../types";
|
|
11
|
+
import type { Signers } from "./types";
|
|
12
|
+
|
|
13
|
+
async function deployFixture() {
|
|
14
|
+
const factory = (await ethers.getContractFactory(
|
|
15
|
+
"FHEAccessControl"
|
|
16
|
+
)) as FHEAccessControl__factory;
|
|
17
|
+
const contract = (await factory.deploy()) as FHEAccessControl;
|
|
18
|
+
const contractAddress = await contract.getAddress();
|
|
19
|
+
return { contract, contractAddress };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @notice Tests FHE access control patterns
|
|
24
|
+
* Demonstrates correct and incorrect permission handling
|
|
25
|
+
*/
|
|
26
|
+
describe("FHEAccessControl", function () {
|
|
27
|
+
let contract: FHEAccessControl;
|
|
28
|
+
let contractAddress: string;
|
|
29
|
+
let signers: Signers;
|
|
30
|
+
let bob: HardhatEthersSigner;
|
|
31
|
+
|
|
32
|
+
before(async function () {
|
|
33
|
+
if (!hre.fhevm.isMock) {
|
|
34
|
+
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
|
|
35
|
+
}
|
|
36
|
+
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
|
|
37
|
+
signers = { owner: ethSigners[0], alice: ethSigners[1] };
|
|
38
|
+
bob = ethSigners[2];
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
beforeEach(async function () {
|
|
42
|
+
const deployment = await deployFixture();
|
|
43
|
+
contractAddress = deployment.contractAddress;
|
|
44
|
+
contract = deployment.contract;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("Correct Access Control Pattern", function () {
|
|
48
|
+
const secretValue = 42;
|
|
49
|
+
|
|
50
|
+
it("should allow user decryption with full access", async function () {
|
|
51
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
52
|
+
|
|
53
|
+
// Store value with proper permissions
|
|
54
|
+
const input = await fhevm
|
|
55
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
56
|
+
.add32(secretValue)
|
|
57
|
+
.encrypt();
|
|
58
|
+
await contract
|
|
59
|
+
.connect(signers.alice)
|
|
60
|
+
.storeWithFullAccess(input.handles[0], input.inputProof);
|
|
61
|
+
|
|
62
|
+
// Verify access was granted
|
|
63
|
+
expect(await contract.hasAccess(signers.alice.address)).to.equal(true);
|
|
64
|
+
|
|
65
|
+
// User should be able to decrypt
|
|
66
|
+
const encrypted = await contract.getSecretValue();
|
|
67
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
68
|
+
FhevmType.euint32,
|
|
69
|
+
encrypted,
|
|
70
|
+
contractAddress,
|
|
71
|
+
signers.alice
|
|
72
|
+
);
|
|
73
|
+
expect(decrypted).to.equal(secretValue);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should allow granting access to additional users", async function () {
|
|
77
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
78
|
+
|
|
79
|
+
// Alice stores the value
|
|
80
|
+
const input = await fhevm
|
|
81
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
82
|
+
.add32(secretValue)
|
|
83
|
+
.encrypt();
|
|
84
|
+
await contract
|
|
85
|
+
.connect(signers.alice)
|
|
86
|
+
.storeWithFullAccess(input.handles[0], input.inputProof);
|
|
87
|
+
|
|
88
|
+
// Alice grants access to Bob
|
|
89
|
+
await contract.connect(signers.alice).grantAccess(bob.address);
|
|
90
|
+
|
|
91
|
+
// Bob can now decrypt
|
|
92
|
+
const encrypted = await contract.getSecretValue();
|
|
93
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
94
|
+
FhevmType.euint32,
|
|
95
|
+
encrypted,
|
|
96
|
+
contractAddress,
|
|
97
|
+
bob
|
|
98
|
+
);
|
|
99
|
+
expect(decrypted).to.equal(secretValue);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("Wrong Access Control Patterns", function () {
|
|
104
|
+
const secretValue = 42;
|
|
105
|
+
|
|
106
|
+
it("should FAIL user decryption without allowThis", async function () {
|
|
107
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
108
|
+
|
|
109
|
+
// Store value WITHOUT allowThis (wrong pattern)
|
|
110
|
+
const input = await fhevm
|
|
111
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
112
|
+
.add32(secretValue)
|
|
113
|
+
.encrypt();
|
|
114
|
+
await contract
|
|
115
|
+
.connect(signers.alice)
|
|
116
|
+
.storeWithoutAllowThis(input.handles[0], input.inputProof);
|
|
117
|
+
|
|
118
|
+
// Attempting to decrypt should fail
|
|
119
|
+
const encrypted = await contract.getSecretValue();
|
|
120
|
+
await expect(
|
|
121
|
+
fhevm.userDecryptEuint(
|
|
122
|
+
FhevmType.euint32,
|
|
123
|
+
encrypted,
|
|
124
|
+
contractAddress,
|
|
125
|
+
signers.alice
|
|
126
|
+
)
|
|
127
|
+
).to.be.rejected;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should FAIL user decryption without user allow", async function () {
|
|
131
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
132
|
+
|
|
133
|
+
// Store value WITHOUT user allow (wrong pattern)
|
|
134
|
+
const input = await fhevm
|
|
135
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
136
|
+
.add32(secretValue)
|
|
137
|
+
.encrypt();
|
|
138
|
+
await contract
|
|
139
|
+
.connect(signers.alice)
|
|
140
|
+
.storeWithoutUserAllow(input.handles[0], input.inputProof);
|
|
141
|
+
|
|
142
|
+
// User (alice) cannot decrypt because no permission was granted
|
|
143
|
+
const encrypted = await contract.getSecretValue();
|
|
144
|
+
await expect(
|
|
145
|
+
fhevm.userDecryptEuint(
|
|
146
|
+
FhevmType.euint32,
|
|
147
|
+
encrypted,
|
|
148
|
+
contractAddress,
|
|
149
|
+
signers.alice
|
|
150
|
+
)
|
|
151
|
+
).to.be.rejected;
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
|
|
2
|
+
import { expect } from "chai";
|
|
3
|
+
import { ethers } from "hardhat";
|
|
4
|
+
import * as hre from "hardhat";
|
|
5
|
+
|
|
6
|
+
import { FHEAntiPatterns, FHEAntiPatterns__factory } from "../types";
|
|
7
|
+
import type { Signers } from "./types";
|
|
8
|
+
|
|
9
|
+
async function deployFixture() {
|
|
10
|
+
const factory = (await ethers.getContractFactory(
|
|
11
|
+
"FHEAntiPatterns"
|
|
12
|
+
)) as FHEAntiPatterns__factory;
|
|
13
|
+
const contract = (await factory.deploy()) as FHEAntiPatterns;
|
|
14
|
+
const contractAddress = await contract.getAddress();
|
|
15
|
+
return { contract, contractAddress };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @notice Tests for FHE anti-patterns
|
|
20
|
+
* Demonstrates correct patterns for common FHE mistakes
|
|
21
|
+
*/
|
|
22
|
+
describe("FHEAntiPatterns", function () {
|
|
23
|
+
let contract: FHEAntiPatterns;
|
|
24
|
+
let contractAddress: string;
|
|
25
|
+
let signers: Signers;
|
|
26
|
+
|
|
27
|
+
before(async function () {
|
|
28
|
+
if (!hre.fhevm.isMock) {
|
|
29
|
+
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
|
|
30
|
+
}
|
|
31
|
+
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
|
|
32
|
+
signers = { owner: ethSigners[0], alice: ethSigners[1] };
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
beforeEach(async function () {
|
|
36
|
+
const deployment = await deployFixture();
|
|
37
|
+
contractAddress = deployment.contractAddress;
|
|
38
|
+
contract = deployment.contract;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("Initialization", function () {
|
|
42
|
+
it("should initialize with encrypted balance and threshold", async function () {
|
|
43
|
+
const fhevm = hre.fhevm;
|
|
44
|
+
const balance = 100;
|
|
45
|
+
const threshold = 50;
|
|
46
|
+
|
|
47
|
+
const input = await fhevm
|
|
48
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
49
|
+
.add32(balance)
|
|
50
|
+
.add32(threshold)
|
|
51
|
+
.encrypt();
|
|
52
|
+
|
|
53
|
+
await contract
|
|
54
|
+
.connect(signers.alice)
|
|
55
|
+
.initialize(input.handles[0], input.handles[1], input.inputProof);
|
|
56
|
+
|
|
57
|
+
// Contract should now have encrypted values
|
|
58
|
+
// We can't directly verify values without decryption, but tx should succeed
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("Correct Conditional Pattern", function () {
|
|
63
|
+
it("should handle conditional logic with FHE.select", async function () {
|
|
64
|
+
const fhevm = hre.fhevm;
|
|
65
|
+
|
|
66
|
+
// Initialize with balance=100, threshold=50
|
|
67
|
+
const input = await fhevm
|
|
68
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
69
|
+
.add32(100) // balance
|
|
70
|
+
.add32(50) // threshold
|
|
71
|
+
.encrypt();
|
|
72
|
+
|
|
73
|
+
await contract
|
|
74
|
+
.connect(signers.alice)
|
|
75
|
+
.initialize(input.handles[0], input.handles[1], input.inputProof);
|
|
76
|
+
|
|
77
|
+
// Execute correct conditional - should not revert
|
|
78
|
+
await expect(contract.connect(signers.alice).correctConditional()).to.not
|
|
79
|
+
.be.reverted;
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("Correct Computation Pattern", function () {
|
|
84
|
+
it("should compute with proper permission grants", async function () {
|
|
85
|
+
const fhevm = hre.fhevm;
|
|
86
|
+
|
|
87
|
+
const input = await fhevm
|
|
88
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
89
|
+
.add32(50) // balance
|
|
90
|
+
.add32(25) // threshold
|
|
91
|
+
.encrypt();
|
|
92
|
+
|
|
93
|
+
await contract
|
|
94
|
+
.connect(signers.alice)
|
|
95
|
+
.initialize(input.handles[0], input.handles[1], input.inputProof);
|
|
96
|
+
|
|
97
|
+
// correctCompute grants proper permissions
|
|
98
|
+
await expect(contract.connect(signers.alice).correctCompute()).to.not.be
|
|
99
|
+
.reverted;
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("Rules Reference", function () {
|
|
104
|
+
it("should return the key rules summary", async function () {
|
|
105
|
+
const rules = await contract.getRules();
|
|
106
|
+
expect(rules).to.include("FHE.select");
|
|
107
|
+
expect(rules).to.include("FHE.allowThis");
|
|
108
|
+
expect(rules).to.include("require/revert");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FhevmType,
|
|
3
|
+
HardhatFhevmRuntimeEnvironment,
|
|
4
|
+
} from "@fhevm/hardhat-plugin";
|
|
5
|
+
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
|
|
6
|
+
import { expect } from "chai";
|
|
7
|
+
import { ethers } from "hardhat";
|
|
8
|
+
import * as hre from "hardhat";
|
|
9
|
+
|
|
10
|
+
import { FHEHandles, FHEHandles__factory } from "../types";
|
|
11
|
+
import type { Signers } from "./types";
|
|
12
|
+
|
|
13
|
+
async function deployFixture() {
|
|
14
|
+
const factory = (await ethers.getContractFactory(
|
|
15
|
+
"FHEHandles"
|
|
16
|
+
)) as FHEHandles__factory;
|
|
17
|
+
const contract = (await factory.deploy()) as FHEHandles;
|
|
18
|
+
const contractAddress = await contract.getAddress();
|
|
19
|
+
return { contract, contractAddress };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @notice Tests FHE handle lifecycle and operations
|
|
24
|
+
* Demonstrates handle creation, computation, and immutability
|
|
25
|
+
*/
|
|
26
|
+
describe("FHEHandles", function () {
|
|
27
|
+
let contract: FHEHandles;
|
|
28
|
+
let contractAddress: string;
|
|
29
|
+
let signers: Signers;
|
|
30
|
+
|
|
31
|
+
before(async function () {
|
|
32
|
+
if (!hre.fhevm.isMock) {
|
|
33
|
+
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
|
|
34
|
+
}
|
|
35
|
+
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
|
|
36
|
+
signers = { owner: ethSigners[0], alice: ethSigners[1] };
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
beforeEach(async function () {
|
|
40
|
+
const deployment = await deployFixture();
|
|
41
|
+
contractAddress = deployment.contractAddress;
|
|
42
|
+
contract = deployment.contract;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe("Handle Creation", function () {
|
|
46
|
+
it("should create handle from external encrypted input", async function () {
|
|
47
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
48
|
+
const value = 42;
|
|
49
|
+
|
|
50
|
+
const input = await fhevm
|
|
51
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
52
|
+
.add32(value)
|
|
53
|
+
.encrypt();
|
|
54
|
+
|
|
55
|
+
await contract
|
|
56
|
+
.connect(signers.alice)
|
|
57
|
+
.createFromExternal(input.handles[0], input.inputProof);
|
|
58
|
+
|
|
59
|
+
const encrypted = await contract.getStoredValue();
|
|
60
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
61
|
+
FhevmType.euint32,
|
|
62
|
+
encrypted,
|
|
63
|
+
contractAddress,
|
|
64
|
+
signers.alice
|
|
65
|
+
);
|
|
66
|
+
expect(decrypted).to.equal(value);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should create handle from plaintext constant", async function () {
|
|
70
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
71
|
+
const plaintextValue = 100;
|
|
72
|
+
|
|
73
|
+
await contract.connect(signers.alice).createFromPlaintext(plaintextValue);
|
|
74
|
+
|
|
75
|
+
const encrypted = await contract.getStoredValue();
|
|
76
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
77
|
+
FhevmType.euint32,
|
|
78
|
+
encrypted,
|
|
79
|
+
contractAddress,
|
|
80
|
+
signers.alice
|
|
81
|
+
);
|
|
82
|
+
expect(decrypted).to.equal(plaintextValue);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("Handle Computation", function () {
|
|
87
|
+
const initialValue = 50;
|
|
88
|
+
|
|
89
|
+
beforeEach(async function () {
|
|
90
|
+
await contract.connect(signers.alice).createFromPlaintext(initialValue);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should create new handle when computing", async function () {
|
|
94
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
95
|
+
|
|
96
|
+
// computeNewHandle adds 10 to stored value
|
|
97
|
+
await contract.connect(signers.alice).computeNewHandle();
|
|
98
|
+
|
|
99
|
+
const encrypted = await contract.getComputedValue();
|
|
100
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
101
|
+
FhevmType.euint32,
|
|
102
|
+
encrypted,
|
|
103
|
+
contractAddress,
|
|
104
|
+
signers.alice
|
|
105
|
+
);
|
|
106
|
+
expect(decrypted).to.equal(initialValue + 10);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should handle chained operations correctly", async function () {
|
|
110
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
111
|
+
|
|
112
|
+
// chainedOperations: (value + 5) * 2 - 1
|
|
113
|
+
// (50 + 5) * 2 - 1 = 55 * 2 - 1 = 110 - 1 = 109
|
|
114
|
+
await contract.connect(signers.alice).chainedOperations();
|
|
115
|
+
|
|
116
|
+
const encrypted = await contract.getComputedValue();
|
|
117
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
118
|
+
FhevmType.euint32,
|
|
119
|
+
encrypted,
|
|
120
|
+
contractAddress,
|
|
121
|
+
signers.alice
|
|
122
|
+
);
|
|
123
|
+
expect(decrypted).to.equal(109);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("Handle Immutability", function () {
|
|
128
|
+
it("should demonstrate handle immutability", async function () {
|
|
129
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
130
|
+
const initialValue = 100;
|
|
131
|
+
|
|
132
|
+
await contract.connect(signers.alice).createFromPlaintext(initialValue);
|
|
133
|
+
|
|
134
|
+
// Get original value before update
|
|
135
|
+
const originalEncrypted = await contract.getStoredValue();
|
|
136
|
+
|
|
137
|
+
// This updates _storedValue to (old + 100)
|
|
138
|
+
await contract.connect(signers.alice).demonstrateImmutability();
|
|
139
|
+
|
|
140
|
+
// Get new value
|
|
141
|
+
const newEncrypted = await contract.getStoredValue();
|
|
142
|
+
const newDecrypted = await fhevm.userDecryptEuint(
|
|
143
|
+
FhevmType.euint32,
|
|
144
|
+
newEncrypted,
|
|
145
|
+
contractAddress,
|
|
146
|
+
signers.alice
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// New value should be initialValue + 100
|
|
150
|
+
expect(newDecrypted).to.equal(initialValue + 100);
|
|
151
|
+
|
|
152
|
+
// The handles should be different (different encrypted values)
|
|
153
|
+
// Note: In mock mode, we can't directly compare handle values
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FhevmType,
|
|
3
|
+
HardhatFhevmRuntimeEnvironment,
|
|
4
|
+
} from "@fhevm/hardhat-plugin";
|
|
5
|
+
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
|
|
6
|
+
import { expect } from "chai";
|
|
7
|
+
import { ethers } from "hardhat";
|
|
8
|
+
import * as hre from "hardhat";
|
|
9
|
+
|
|
10
|
+
import { FHEInputProof, FHEInputProof__factory } from "../types";
|
|
11
|
+
import type { Signers } from "./types";
|
|
12
|
+
|
|
13
|
+
async function deployFixture() {
|
|
14
|
+
const factory = (await ethers.getContractFactory(
|
|
15
|
+
"FHEInputProof"
|
|
16
|
+
)) as FHEInputProof__factory;
|
|
17
|
+
const contract = (await factory.deploy()) as FHEInputProof;
|
|
18
|
+
const contractAddress = await contract.getAddress();
|
|
19
|
+
return { contract, contractAddress };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @notice Tests input proof validation patterns
|
|
24
|
+
* Demonstrates single and multiple encrypted inputs with proofs
|
|
25
|
+
*/
|
|
26
|
+
describe("FHEInputProof", function () {
|
|
27
|
+
let contract: FHEInputProof;
|
|
28
|
+
let contractAddress: string;
|
|
29
|
+
let signers: Signers;
|
|
30
|
+
|
|
31
|
+
before(async function () {
|
|
32
|
+
if (!hre.fhevm.isMock) {
|
|
33
|
+
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
|
|
34
|
+
}
|
|
35
|
+
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
|
|
36
|
+
signers = { owner: ethSigners[0], alice: ethSigners[1] };
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
beforeEach(async function () {
|
|
40
|
+
const deployment = await deployFixture();
|
|
41
|
+
contractAddress = deployment.contractAddress;
|
|
42
|
+
contract = deployment.contract;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe("Single Input with Proof", function () {
|
|
46
|
+
it("should accept and store a single encrypted value with valid proof", async function () {
|
|
47
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
48
|
+
const secretValue = 123;
|
|
49
|
+
|
|
50
|
+
// Create encrypted input with proof
|
|
51
|
+
const encryptedInput = await fhevm
|
|
52
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
53
|
+
.add32(secretValue)
|
|
54
|
+
.encrypt();
|
|
55
|
+
|
|
56
|
+
// Submit to contract - proof is validated in fromExternal
|
|
57
|
+
await contract
|
|
58
|
+
.connect(signers.alice)
|
|
59
|
+
.setSingleValue(encryptedInput.handles[0], encryptedInput.inputProof);
|
|
60
|
+
|
|
61
|
+
// Verify the value was stored correctly
|
|
62
|
+
const encrypted = await contract.getSingleValue();
|
|
63
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
64
|
+
FhevmType.euint32,
|
|
65
|
+
encrypted,
|
|
66
|
+
contractAddress,
|
|
67
|
+
signers.alice
|
|
68
|
+
);
|
|
69
|
+
expect(decrypted).to.equal(secretValue);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("Multiple Inputs with Batched Proof", function () {
|
|
74
|
+
it("should accept multiple encrypted values with a single proof", async function () {
|
|
75
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
76
|
+
const valueA = 100;
|
|
77
|
+
const valueB = 200n; // uint64
|
|
78
|
+
|
|
79
|
+
// Create batched encrypted input
|
|
80
|
+
const encryptedInput = await fhevm
|
|
81
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
82
|
+
.add32(valueA) // index 0 -> handles[0]
|
|
83
|
+
.add64(valueB) // index 1 -> handles[1]
|
|
84
|
+
.encrypt();
|
|
85
|
+
|
|
86
|
+
// Submit both values with single proof
|
|
87
|
+
await contract
|
|
88
|
+
.connect(signers.alice)
|
|
89
|
+
.setMultipleValues(
|
|
90
|
+
encryptedInput.handles[0],
|
|
91
|
+
encryptedInput.handles[1],
|
|
92
|
+
encryptedInput.inputProof
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Verify both values
|
|
96
|
+
const encryptedA = await contract.getValueA();
|
|
97
|
+
const decryptedA = await fhevm.userDecryptEuint(
|
|
98
|
+
FhevmType.euint32,
|
|
99
|
+
encryptedA,
|
|
100
|
+
contractAddress,
|
|
101
|
+
signers.alice
|
|
102
|
+
);
|
|
103
|
+
expect(decryptedA).to.equal(valueA);
|
|
104
|
+
|
|
105
|
+
const encryptedB = await contract.getValueB();
|
|
106
|
+
const decryptedB = await fhevm.userDecryptEuint(
|
|
107
|
+
FhevmType.euint64,
|
|
108
|
+
encryptedB,
|
|
109
|
+
contractAddress,
|
|
110
|
+
signers.alice
|
|
111
|
+
);
|
|
112
|
+
expect(decryptedB).to.equal(valueB);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("Computation with New Input Proof", function () {
|
|
117
|
+
it("should add an encrypted value to stored value", async function () {
|
|
118
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
119
|
+
const initialValue = 100;
|
|
120
|
+
const addendValue = 50;
|
|
121
|
+
|
|
122
|
+
// Set initial value
|
|
123
|
+
const input1 = await fhevm
|
|
124
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
125
|
+
.add32(initialValue)
|
|
126
|
+
.encrypt();
|
|
127
|
+
await contract
|
|
128
|
+
.connect(signers.alice)
|
|
129
|
+
.setSingleValue(input1.handles[0], input1.inputProof);
|
|
130
|
+
|
|
131
|
+
// Add another encrypted value
|
|
132
|
+
const input2 = await fhevm
|
|
133
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
134
|
+
.add32(addendValue)
|
|
135
|
+
.encrypt();
|
|
136
|
+
await contract
|
|
137
|
+
.connect(signers.alice)
|
|
138
|
+
.addToValue(input2.handles[0], input2.inputProof);
|
|
139
|
+
|
|
140
|
+
// Verify result
|
|
141
|
+
const encrypted = await contract.getSingleValue();
|
|
142
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
143
|
+
FhevmType.euint32,
|
|
144
|
+
encrypted,
|
|
145
|
+
contractAddress,
|
|
146
|
+
signers.alice
|
|
147
|
+
);
|
|
148
|
+
expect(decrypted).to.equal(initialValue + addendValue);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|