create-fhevm-example 1.4.5 → 1.4.6
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/README.md +3 -4
- package/contracts/concepts/antipatterns/ControlFlow.sol +160 -0
- package/contracts/concepts/antipatterns/OperationsGasNoise.sol +190 -0
- package/contracts/concepts/antipatterns/Permissions.sol +254 -0
- package/dist/scripts/commands/add-mode.d.ts.map +1 -1
- package/dist/scripts/commands/add-mode.js +10 -21
- package/dist/scripts/commands/doctor.js +4 -1
- package/dist/scripts/commands/generate-docs.js +4 -1
- package/dist/scripts/index.js +43 -24
- package/dist/scripts/shared/builders.d.ts.map +1 -1
- package/dist/scripts/shared/builders.js +8 -14
- package/dist/scripts/shared/config.d.ts.map +1 -1
- package/dist/scripts/shared/config.js +41 -12
- package/dist/scripts/shared/utils.js +2 -2
- package/package.json +1 -1
- package/test/concepts/FHEAntiPatterns.ts +2 -1
- package/test/concepts/antipatterns/ControlFlow.ts +125 -0
- package/test/concepts/antipatterns/OperationsGasNoise.ts +187 -0
- package/test/concepts/antipatterns/Permissions.ts +327 -0
- package/contracts/concepts/FHEAntiPatterns.sol +0 -300
|
@@ -0,0 +1,187 @@
|
|
|
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 {
|
|
11
|
+
FHEOperationsGasNoiseAntiPatterns,
|
|
12
|
+
FHEOperationsGasNoiseAntiPatterns__factory,
|
|
13
|
+
} from "../types";
|
|
14
|
+
import type { Signers } from "./types";
|
|
15
|
+
|
|
16
|
+
async function deployFixture() {
|
|
17
|
+
const factory = (await ethers.getContractFactory(
|
|
18
|
+
"FHEOperationsGasNoiseAntiPatterns"
|
|
19
|
+
)) as FHEOperationsGasNoiseAntiPatterns__factory;
|
|
20
|
+
const contract =
|
|
21
|
+
(await factory.deploy()) as FHEOperationsGasNoiseAntiPatterns;
|
|
22
|
+
const contractAddress = await contract.getAddress();
|
|
23
|
+
return { contract, contractAddress };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @notice Tests for FHE operations, gas, and noise anti-patterns
|
|
28
|
+
* Demonstrates performance issues and optimization techniques
|
|
29
|
+
*/
|
|
30
|
+
describe("FHEOperationsGasNoiseAntiPatterns", function () {
|
|
31
|
+
let contract: FHEOperationsGasNoiseAntiPatterns;
|
|
32
|
+
let contractAddress: string;
|
|
33
|
+
let signers: Signers;
|
|
34
|
+
|
|
35
|
+
before(async function () {
|
|
36
|
+
if (!hre.fhevm.isMock) {
|
|
37
|
+
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
|
|
38
|
+
}
|
|
39
|
+
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
|
|
40
|
+
signers = { owner: ethSigners[0], alice: ethSigners[1] };
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
beforeEach(async function () {
|
|
44
|
+
const deployment = await deployFixture();
|
|
45
|
+
contractAddress = deployment.contractAddress;
|
|
46
|
+
contract = deployment.contract;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("Pattern 1: Gas/Timing Side Channel", function () {
|
|
50
|
+
const testValue = 50;
|
|
51
|
+
|
|
52
|
+
it("wrongGasLeak should store value", async function () {
|
|
53
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
54
|
+
|
|
55
|
+
const input = await fhevm
|
|
56
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
57
|
+
.add32(testValue)
|
|
58
|
+
.encrypt();
|
|
59
|
+
|
|
60
|
+
await contract
|
|
61
|
+
.connect(signers.alice)
|
|
62
|
+
.wrongGasLeak(input.handles[0], input.inputProof);
|
|
63
|
+
|
|
64
|
+
// Value should be stored (but without permission to decrypt)
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("correctConstantTime should use constant gas", async function () {
|
|
68
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
69
|
+
|
|
70
|
+
const input = await fhevm
|
|
71
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
72
|
+
.add32(testValue)
|
|
73
|
+
.encrypt();
|
|
74
|
+
|
|
75
|
+
await contract
|
|
76
|
+
.connect(signers.alice)
|
|
77
|
+
.correctConstantTime(input.handles[0], input.inputProof);
|
|
78
|
+
|
|
79
|
+
const encrypted = await contract.getValue();
|
|
80
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
81
|
+
FhevmType.euint32,
|
|
82
|
+
encrypted,
|
|
83
|
+
contractAddress,
|
|
84
|
+
signers.alice
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(decrypted).to.equal(testValue * 2);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("Pattern 2: Noise Accumulation", function () {
|
|
92
|
+
const testValue = 2;
|
|
93
|
+
|
|
94
|
+
it("wrongNoiseAccumulation should chain many operations", async function () {
|
|
95
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
96
|
+
|
|
97
|
+
const input = await fhevm
|
|
98
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
99
|
+
.add32(testValue)
|
|
100
|
+
.encrypt();
|
|
101
|
+
|
|
102
|
+
await contract
|
|
103
|
+
.connect(signers.alice)
|
|
104
|
+
.wrongNoiseAccumulation(input.handles[0], input.inputProof);
|
|
105
|
+
|
|
106
|
+
// Result stored but may have accumulated noise
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("correctMinimizeOperations should use single operation", async function () {
|
|
110
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
111
|
+
|
|
112
|
+
const input = await fhevm
|
|
113
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
114
|
+
.add32(testValue)
|
|
115
|
+
.encrypt();
|
|
116
|
+
|
|
117
|
+
await contract
|
|
118
|
+
.connect(signers.alice)
|
|
119
|
+
.correctMinimizeOperations(input.handles[0], input.inputProof);
|
|
120
|
+
|
|
121
|
+
const encrypted = await contract.getValue();
|
|
122
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
123
|
+
FhevmType.euint32,
|
|
124
|
+
encrypted,
|
|
125
|
+
contractAddress,
|
|
126
|
+
signers.alice
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// 2 * 32 = 64
|
|
130
|
+
expect(decrypted).to.equal(64);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("Pattern 3: Deprecated APIs", function () {
|
|
135
|
+
it("wrongDeprecatedAPI should return warning string", async function () {
|
|
136
|
+
const result = await contract.wrongDeprecatedAPI();
|
|
137
|
+
expect(result).to.include("deprecated");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("correctModernAPI should return guidance string", async function () {
|
|
141
|
+
const result = await contract.correctModernAPI();
|
|
142
|
+
expect(result).to.include("FHE.makePubliclyDecryptable");
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe("Pattern 4: Type Mismatch", function () {
|
|
147
|
+
const testValue = 100;
|
|
148
|
+
|
|
149
|
+
it("wrongOversizedType should use euint256 unnecessarily", async function () {
|
|
150
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
151
|
+
|
|
152
|
+
const input = await fhevm
|
|
153
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
154
|
+
.add32(testValue)
|
|
155
|
+
.encrypt();
|
|
156
|
+
|
|
157
|
+
await contract
|
|
158
|
+
.connect(signers.alice)
|
|
159
|
+
.wrongOversizedType(input.handles[0], input.inputProof);
|
|
160
|
+
|
|
161
|
+
// Value stored but with wasteful type conversion
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("correctRightSizedType should use euint32", async function () {
|
|
165
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
166
|
+
|
|
167
|
+
const input = await fhevm
|
|
168
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
169
|
+
.add32(testValue)
|
|
170
|
+
.encrypt();
|
|
171
|
+
|
|
172
|
+
await contract
|
|
173
|
+
.connect(signers.alice)
|
|
174
|
+
.correctRightSizedType(input.handles[0], input.inputProof);
|
|
175
|
+
|
|
176
|
+
const encrypted = await contract.getValue();
|
|
177
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
178
|
+
FhevmType.euint32,
|
|
179
|
+
encrypted,
|
|
180
|
+
contractAddress,
|
|
181
|
+
signers.alice
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
expect(decrypted).to.equal(testValue * 2);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
@@ -0,0 +1,327 @@
|
|
|
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 {
|
|
11
|
+
FHEPermissionsAntiPatterns,
|
|
12
|
+
FHEPermissionsAntiPatterns__factory,
|
|
13
|
+
} from "../types";
|
|
14
|
+
import type { Signers } from "./types";
|
|
15
|
+
|
|
16
|
+
async function deployFixture() {
|
|
17
|
+
const factory = (await ethers.getContractFactory(
|
|
18
|
+
"FHEPermissionsAntiPatterns"
|
|
19
|
+
)) as FHEPermissionsAntiPatterns__factory;
|
|
20
|
+
const contract = (await factory.deploy()) as FHEPermissionsAntiPatterns;
|
|
21
|
+
const contractAddress = await contract.getAddress();
|
|
22
|
+
return { contract, contractAddress };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @notice Tests for FHE permission anti-patterns
|
|
27
|
+
* Demonstrates wrong and correct permission handling patterns
|
|
28
|
+
*/
|
|
29
|
+
describe("FHEPermissionsAntiPatterns", function () {
|
|
30
|
+
let contract: FHEPermissionsAntiPatterns;
|
|
31
|
+
let contractAddress: string;
|
|
32
|
+
let signers: Signers;
|
|
33
|
+
let bob: HardhatEthersSigner;
|
|
34
|
+
|
|
35
|
+
before(async function () {
|
|
36
|
+
if (!hre.fhevm.isMock) {
|
|
37
|
+
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
|
|
38
|
+
}
|
|
39
|
+
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
|
|
40
|
+
signers = { owner: ethSigners[0], alice: ethSigners[1] };
|
|
41
|
+
bob = ethSigners[2];
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
beforeEach(async function () {
|
|
45
|
+
const deployment = await deployFixture();
|
|
46
|
+
contractAddress = deployment.contractAddress;
|
|
47
|
+
contract = deployment.contract;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("Pattern 1: Missing allowThis", function () {
|
|
51
|
+
const testValue = 42;
|
|
52
|
+
|
|
53
|
+
it("should FAIL without allowThis", async function () {
|
|
54
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
55
|
+
|
|
56
|
+
const input = await fhevm
|
|
57
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
58
|
+
.add32(testValue)
|
|
59
|
+
.encrypt();
|
|
60
|
+
|
|
61
|
+
await contract
|
|
62
|
+
.connect(signers.alice)
|
|
63
|
+
.wrongMissingAllowThis(input.handles[0], input.inputProof);
|
|
64
|
+
|
|
65
|
+
// Decryption should fail
|
|
66
|
+
const encrypted = await contract.getValue();
|
|
67
|
+
await expect(
|
|
68
|
+
fhevm.userDecryptEuint(
|
|
69
|
+
FhevmType.euint32,
|
|
70
|
+
encrypted,
|
|
71
|
+
contractAddress,
|
|
72
|
+
signers.alice
|
|
73
|
+
)
|
|
74
|
+
).to.be.rejected;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should succeed with allowThis", async function () {
|
|
78
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
79
|
+
|
|
80
|
+
const input = await fhevm
|
|
81
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
82
|
+
.add32(testValue)
|
|
83
|
+
.encrypt();
|
|
84
|
+
|
|
85
|
+
await contract
|
|
86
|
+
.connect(signers.alice)
|
|
87
|
+
.correctWithAllowThis(input.handles[0], input.inputProof);
|
|
88
|
+
|
|
89
|
+
const encrypted = await contract.getValue();
|
|
90
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
91
|
+
FhevmType.euint32,
|
|
92
|
+
encrypted,
|
|
93
|
+
contractAddress,
|
|
94
|
+
signers.alice
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(decrypted).to.equal(testValue * 2);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("Pattern 2: Missing allow(user)", function () {
|
|
102
|
+
const testValue = 100;
|
|
103
|
+
|
|
104
|
+
it("should FAIL without user allow", async function () {
|
|
105
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
106
|
+
|
|
107
|
+
const input = await fhevm
|
|
108
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
109
|
+
.add32(testValue)
|
|
110
|
+
.encrypt();
|
|
111
|
+
|
|
112
|
+
await contract
|
|
113
|
+
.connect(signers.alice)
|
|
114
|
+
.wrongMissingUserAllow(input.handles[0], input.inputProof);
|
|
115
|
+
|
|
116
|
+
const encrypted = await contract.getValue();
|
|
117
|
+
await expect(
|
|
118
|
+
fhevm.userDecryptEuint(
|
|
119
|
+
FhevmType.euint32,
|
|
120
|
+
encrypted,
|
|
121
|
+
contractAddress,
|
|
122
|
+
signers.alice
|
|
123
|
+
)
|
|
124
|
+
).to.be.rejected;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should succeed with user allow", async function () {
|
|
128
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
129
|
+
|
|
130
|
+
const input = await fhevm
|
|
131
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
132
|
+
.add32(testValue)
|
|
133
|
+
.encrypt();
|
|
134
|
+
|
|
135
|
+
await contract
|
|
136
|
+
.connect(signers.alice)
|
|
137
|
+
.correctWithUserAllow(input.handles[0], input.inputProof);
|
|
138
|
+
|
|
139
|
+
const encrypted = await contract.getValue();
|
|
140
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
141
|
+
FhevmType.euint32,
|
|
142
|
+
encrypted,
|
|
143
|
+
contractAddress,
|
|
144
|
+
signers.alice
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
expect(decrypted).to.equal(testValue);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("Pattern 3: View Function Without Permissions", function () {
|
|
152
|
+
const testValue = 200;
|
|
153
|
+
|
|
154
|
+
it("should FAIL when stored without permission", async function () {
|
|
155
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
156
|
+
|
|
157
|
+
const input = await fhevm
|
|
158
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
159
|
+
.add32(testValue)
|
|
160
|
+
.encrypt();
|
|
161
|
+
|
|
162
|
+
await contract
|
|
163
|
+
.connect(signers.alice)
|
|
164
|
+
.wrongStoreWithoutPermission(input.handles[0], input.inputProof);
|
|
165
|
+
|
|
166
|
+
const encrypted = await contract.getValue();
|
|
167
|
+
await expect(
|
|
168
|
+
fhevm.userDecryptEuint(
|
|
169
|
+
FhevmType.euint32,
|
|
170
|
+
encrypted,
|
|
171
|
+
contractAddress,
|
|
172
|
+
signers.alice
|
|
173
|
+
)
|
|
174
|
+
).to.be.rejected;
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("should succeed when stored with permission", async function () {
|
|
178
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
179
|
+
|
|
180
|
+
const input = await fhevm
|
|
181
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
182
|
+
.add32(testValue)
|
|
183
|
+
.encrypt();
|
|
184
|
+
|
|
185
|
+
await contract
|
|
186
|
+
.connect(signers.alice)
|
|
187
|
+
.correctStoreWithPermission(input.handles[0], input.inputProof);
|
|
188
|
+
|
|
189
|
+
const encrypted = await contract.getValue();
|
|
190
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
191
|
+
FhevmType.euint32,
|
|
192
|
+
encrypted,
|
|
193
|
+
contractAddress,
|
|
194
|
+
signers.alice
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
expect(decrypted).to.equal(testValue);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe("Pattern 4: Unauthenticated Re-encryption", function () {
|
|
202
|
+
it("wrongReencryptWithoutAuth should return empty bytes", async function () {
|
|
203
|
+
const dummyPublicKey =
|
|
204
|
+
"0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
205
|
+
const result = await contract.wrongReencryptWithoutAuth(dummyPublicKey);
|
|
206
|
+
expect(result).to.equal("0x");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("correctReencryptWithAuth should return encrypted handle", async function () {
|
|
210
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
211
|
+
|
|
212
|
+
const input = await fhevm
|
|
213
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
214
|
+
.add32(123)
|
|
215
|
+
.encrypt();
|
|
216
|
+
|
|
217
|
+
await contract
|
|
218
|
+
.connect(signers.alice)
|
|
219
|
+
.correctWithUserAllow(input.handles[0], input.inputProof);
|
|
220
|
+
|
|
221
|
+
const encrypted = await contract.correctReencryptWithAuth();
|
|
222
|
+
expect(encrypted).to.not.equal(0);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe("Pattern 5: Transfer Without Permission", function () {
|
|
227
|
+
const initialBalance = 1000;
|
|
228
|
+
const transferAmount = 100;
|
|
229
|
+
|
|
230
|
+
beforeEach(async function () {
|
|
231
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
232
|
+
|
|
233
|
+
// Initialize Alice's balance
|
|
234
|
+
const input = await fhevm
|
|
235
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
236
|
+
.add32(initialBalance)
|
|
237
|
+
.encrypt();
|
|
238
|
+
|
|
239
|
+
await contract
|
|
240
|
+
.connect(signers.alice)
|
|
241
|
+
.initializeBalance(input.handles[0], input.inputProof);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should FAIL recipient decryption without permission", async function () {
|
|
245
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
246
|
+
|
|
247
|
+
const input = await fhevm
|
|
248
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
249
|
+
.add32(transferAmount)
|
|
250
|
+
.encrypt();
|
|
251
|
+
|
|
252
|
+
await contract
|
|
253
|
+
.connect(signers.alice)
|
|
254
|
+
.wrongTransferWithoutPermission(
|
|
255
|
+
bob.address,
|
|
256
|
+
input.handles[0],
|
|
257
|
+
input.inputProof
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
// Bob cannot decrypt his balance
|
|
261
|
+
const encrypted = await contract.getBalance(bob.address);
|
|
262
|
+
await expect(
|
|
263
|
+
fhevm.userDecryptEuint(
|
|
264
|
+
FhevmType.euint32,
|
|
265
|
+
encrypted,
|
|
266
|
+
contractAddress,
|
|
267
|
+
bob
|
|
268
|
+
)
|
|
269
|
+
).to.be.rejected;
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it("should succeed with permission propagation", async function () {
|
|
273
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
274
|
+
|
|
275
|
+
const input = await fhevm
|
|
276
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
277
|
+
.add32(transferAmount)
|
|
278
|
+
.encrypt();
|
|
279
|
+
|
|
280
|
+
await contract
|
|
281
|
+
.connect(signers.alice)
|
|
282
|
+
.correctTransferWithPermission(
|
|
283
|
+
bob.address,
|
|
284
|
+
input.handles[0],
|
|
285
|
+
input.inputProof
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
// Bob can decrypt his balance
|
|
289
|
+
const encrypted = await contract.getBalance(bob.address);
|
|
290
|
+
const decrypted = await fhevm.userDecryptEuint(
|
|
291
|
+
FhevmType.euint32,
|
|
292
|
+
encrypted,
|
|
293
|
+
contractAddress,
|
|
294
|
+
bob
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
expect(decrypted).to.equal(transferAmount);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
describe("Pattern 6: Cross-Contract Permission", function () {
|
|
302
|
+
beforeEach(async function () {
|
|
303
|
+
// Initialize _secretValue so the contract has permission on it
|
|
304
|
+
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
|
|
305
|
+
const input = await fhevm
|
|
306
|
+
.createEncryptedInput(contractAddress, signers.alice.address)
|
|
307
|
+
.add32(42)
|
|
308
|
+
.encrypt();
|
|
309
|
+
|
|
310
|
+
await contract
|
|
311
|
+
.connect(signers.alice)
|
|
312
|
+
.correctWithAllowThis(input.handles[0], input.inputProof);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("wrongCrossContractCall should call without permission", async function () {
|
|
316
|
+
const dummyAddress = "0x0000000000000000000000000000000000000001";
|
|
317
|
+
// This will fail but we're just testing it doesn't revert
|
|
318
|
+
await contract.wrongCrossContractCall(dummyAddress);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("correctCrossContractCall should grant allowTransient", async function () {
|
|
322
|
+
const dummyAddress = "0x0000000000000000000000000000000000000001";
|
|
323
|
+
// This should succeed - contract has permission on _secretValue
|
|
324
|
+
await contract.correctCrossContractCall(dummyAddress);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
});
|