create-fhevm-example 1.4.3 → 1.4.5
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 +22 -57
- package/contracts/advanced/EncryptedEscrow.sol +7 -31
- package/contracts/advanced/HiddenVoting.sol +19 -54
- package/contracts/advanced/PrivateKYC.sol +9 -33
- package/contracts/advanced/PrivatePayroll.sol +9 -35
- package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +19 -21
- package/contracts/basic/decryption/PublicDecryptSingleValue.sol +19 -20
- package/contracts/basic/decryption/UserDecryptMultipleValues.sol +11 -21
- package/contracts/basic/decryption/UserDecryptSingleValue.sol +15 -30
- package/contracts/basic/encryption/EncryptMultipleValues.sol +16 -22
- package/contracts/basic/encryption/EncryptSingleValue.sol +15 -17
- package/contracts/basic/encryption/FHECounter.sol +19 -17
- package/contracts/basic/fhe-operations/FHEAdd.sol +14 -16
- package/contracts/basic/fhe-operations/FHEArithmetic.sol +19 -31
- package/contracts/basic/fhe-operations/FHEComparison.sol +12 -29
- package/contracts/basic/fhe-operations/FHEIfThenElse.sol +9 -10
- package/contracts/concepts/FHEAccessControl.sol +28 -28
- package/contracts/concepts/FHEAntiPatterns.sol +8 -37
- package/contracts/concepts/FHEHandles.sol +14 -29
- package/contracts/concepts/FHEInputProof.sol +13 -33
- package/contracts/gaming/EncryptedLottery.sol +11 -38
- package/contracts/gaming/EncryptedPoker.sol +8 -34
- package/contracts/gaming/RockPaperScissors.sol +43 -64
- package/contracts/openzeppelin/ERC7984.sol +6 -1
- package/contracts/openzeppelin/ERC7984ERC20Wrapper.sol +5 -1
- package/contracts/openzeppelin/SwapERC7984ToERC20.sol +5 -1
- package/contracts/openzeppelin/SwapERC7984ToERC7984.sol +5 -1
- package/contracts/openzeppelin/VestingWallet.sol +13 -17
- package/dist/scripts/commands/generate-config.js +18 -3
- package/dist/scripts/shared/config.d.ts.map +1 -1
- package/dist/scripts/shared/config.js +81 -75
- package/package.json +1 -1
|
@@ -10,33 +10,23 @@ import {
|
|
|
10
10
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* @notice Blind
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* 1. Owner creates auction with end time and minimum bid
|
|
23
|
-
* 2. Bidders submit encrypted bids (one per address)
|
|
24
|
-
* 3. Owner ends auction → winner computed via FHE.gt/select
|
|
25
|
-
* 4. Anyone can reveal winner after decryption proof is ready
|
|
26
|
-
*
|
|
27
|
-
* ⚠️ IMPORTANT: Losing bids remain encrypted forever!
|
|
13
|
+
* @notice Blind auction where bids remain encrypted until the end.
|
|
14
|
+
* Bidders submit encrypted amounts. The contract finds the highest bid
|
|
15
|
+
* using FHE.gt and FHE.select without ever decrypting losing bids.
|
|
16
|
+
* Only the winning bid amount is revealed after the auction closes.
|
|
17
|
+
* Losing bids remain private forever, ensuring true bid confidentiality.
|
|
18
|
+
|
|
19
|
+
* @dev Flow: bid() → endAuction() → revealWinner()
|
|
20
|
+
* Uses FHE.gt/select to find winner without revealing losing bids.
|
|
21
|
+
* Losing bids remain encrypted forever!
|
|
28
22
|
*/
|
|
29
23
|
contract BlindAuction is ZamaEthereumConfig {
|
|
30
|
-
// ==================== TYPES ====================
|
|
31
|
-
|
|
32
24
|
enum AuctionState {
|
|
33
25
|
Open, // Accepting bids
|
|
34
26
|
Closed, // Bidding ended, pending reveal
|
|
35
27
|
Revealed // Winner revealed on-chain
|
|
36
28
|
}
|
|
37
29
|
|
|
38
|
-
// ==================== STATE ====================
|
|
39
|
-
|
|
40
30
|
/// Auction owner (deployer)
|
|
41
31
|
address public owner;
|
|
42
32
|
|
|
@@ -70,8 +60,6 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
70
60
|
/// Revealed winning amount (set after reveal)
|
|
71
61
|
uint64 public winningAmount;
|
|
72
62
|
|
|
73
|
-
// ==================== EVENTS ====================
|
|
74
|
-
|
|
75
63
|
/// @notice Emitted when a new bid is placed
|
|
76
64
|
/// @param bidder Address of the bidder
|
|
77
65
|
event BidPlaced(address indexed bidder);
|
|
@@ -89,16 +77,12 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
89
77
|
/// @param amount Winning bid amount
|
|
90
78
|
event WinnerRevealed(address indexed winner, uint64 amount);
|
|
91
79
|
|
|
92
|
-
// ==================== MODIFIERS ====================
|
|
93
|
-
|
|
94
80
|
/// @dev Restricts function to owner only
|
|
95
81
|
modifier onlyOwner() {
|
|
96
82
|
require(msg.sender == owner, "Only owner can call");
|
|
97
83
|
_;
|
|
98
84
|
}
|
|
99
85
|
|
|
100
|
-
// ==================== CONSTRUCTOR ====================
|
|
101
|
-
|
|
102
86
|
/// @notice Creates a new blind auction
|
|
103
87
|
/// @param _endTime Unix timestamp when bidding ends
|
|
104
88
|
/// @param _minimumBid Minimum bid amount (plaintext)
|
|
@@ -110,11 +94,8 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
110
94
|
auctionState = AuctionState.Open;
|
|
111
95
|
}
|
|
112
96
|
|
|
113
|
-
// ==================== BIDDING ====================
|
|
114
|
-
|
|
115
97
|
/// @notice Submit an encrypted bid to the auction
|
|
116
98
|
/// @dev Each address can only bid once
|
|
117
|
-
/// @param encryptedBid The encrypted bid amount
|
|
118
99
|
/// @param inputProof Proof validating the encrypted input
|
|
119
100
|
function bid(
|
|
120
101
|
externalEuint64 encryptedBid,
|
|
@@ -124,13 +105,11 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
124
105
|
require(block.timestamp < endTime, "Auction has ended");
|
|
125
106
|
require(!hasBid[msg.sender], "Already placed a bid");
|
|
126
107
|
|
|
127
|
-
//
|
|
108
|
+
// Convert external encrypted input to internal handle (proof verified)
|
|
128
109
|
euint64 bidAmount = FHE.fromExternal(encryptedBid, inputProof);
|
|
129
|
-
|
|
130
|
-
// ✅ Grant contract permission to operate on this value
|
|
131
110
|
FHE.allowThis(bidAmount);
|
|
132
111
|
|
|
133
|
-
//
|
|
112
|
+
// Store bid - amount stays encrypted
|
|
134
113
|
_bids[msg.sender] = bidAmount;
|
|
135
114
|
hasBid[msg.sender] = true;
|
|
136
115
|
bidders.push(msg.sender);
|
|
@@ -138,30 +117,24 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
138
117
|
emit BidPlaced(msg.sender);
|
|
139
118
|
}
|
|
140
119
|
|
|
141
|
-
// ==================== END AUCTION ====================
|
|
142
|
-
|
|
143
120
|
/// @notice End the auction and compute the winner
|
|
121
|
+
/// @dev ⚡ Gas: O(n) loop with FHE.gt/select. ~200k gas per bidder!
|
|
144
122
|
/// @dev Only owner can call after end time
|
|
145
123
|
function endAuction() external onlyOwner {
|
|
146
124
|
require(auctionState == AuctionState.Open, "Auction not open");
|
|
147
125
|
require(block.timestamp >= endTime, "Auction not yet ended");
|
|
148
126
|
require(bidders.length > 0, "No bids placed");
|
|
149
127
|
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
// Initialize with first bidder
|
|
128
|
+
// Find winner using encrypted comparisons (no bids revealed!)
|
|
153
129
|
euint64 currentMax = _bids[bidders[0]];
|
|
154
130
|
euint64 currentWinnerIdx = FHE.asEuint64(0);
|
|
155
131
|
|
|
156
|
-
// 🔄 Iterate through remaining bidders
|
|
157
132
|
for (uint256 i = 1; i < bidders.length; i++) {
|
|
158
133
|
euint64 candidateBid = _bids[bidders[i]];
|
|
159
134
|
|
|
160
|
-
//
|
|
161
|
-
//
|
|
135
|
+
// 🔀 Why select? if/else would leak which bid is higher!
|
|
136
|
+
// Losing bids remain encrypted forever
|
|
162
137
|
ebool isGreater = FHE.gt(candidateBid, currentMax);
|
|
163
|
-
|
|
164
|
-
// 🔀 Select the higher bid and its index
|
|
165
138
|
currentMax = FHE.select(isGreater, candidateBid, currentMax);
|
|
166
139
|
currentWinnerIdx = FHE.select(
|
|
167
140
|
isGreater,
|
|
@@ -170,14 +143,12 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
170
143
|
);
|
|
171
144
|
}
|
|
172
145
|
|
|
173
|
-
//
|
|
146
|
+
// Check minimum bid (comparison stays encrypted)
|
|
174
147
|
ebool meetsMinimum = FHE.ge(currentMax, FHE.asEuint64(minimumBid));
|
|
175
|
-
|
|
176
|
-
// If no one meets minimum, winner stays at index 0 but amount becomes 0
|
|
177
148
|
_winningBid = FHE.select(meetsMinimum, currentMax, FHE.asEuint64(0));
|
|
178
149
|
_winnerIndex = currentWinnerIdx;
|
|
179
150
|
|
|
180
|
-
//
|
|
151
|
+
// Mark for public decryption via KMS relayer
|
|
181
152
|
FHE.allowThis(_winningBid);
|
|
182
153
|
FHE.allowThis(_winnerIndex);
|
|
183
154
|
FHE.makePubliclyDecryptable(_winningBid);
|
|
@@ -189,36 +160,32 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
189
160
|
}
|
|
190
161
|
|
|
191
162
|
/// @notice Reveal the winner with KMS decryption proof
|
|
192
|
-
/// @dev Anyone can call
|
|
193
|
-
///
|
|
194
|
-
/// @param decryptionProof KMS signature proving decryption
|
|
163
|
+
/// @dev Anyone can call once relayer provides proof.
|
|
164
|
+
/// ⚠️ Order matters! cts[] must match abi.decode() order.
|
|
195
165
|
function revealWinner(
|
|
196
166
|
bytes memory abiEncodedResults,
|
|
197
167
|
bytes memory decryptionProof
|
|
198
168
|
) external {
|
|
199
169
|
require(auctionState == AuctionState.Closed, "Auction not closed");
|
|
200
170
|
|
|
201
|
-
//
|
|
202
|
-
// Order MUST match abiEncodedResults encoding order!
|
|
171
|
+
// Build handle array - order must match abi.decode() below!
|
|
203
172
|
bytes32[] memory cts = new bytes32[](2);
|
|
204
173
|
cts[0] = FHE.toBytes32(_winningBid);
|
|
205
174
|
cts[1] = FHE.toBytes32(_winnerIndex);
|
|
206
175
|
|
|
207
|
-
//
|
|
176
|
+
// Verify KMS signatures (reverts if proof invalid)
|
|
208
177
|
FHE.checkSignatures(cts, abiEncodedResults, decryptionProof);
|
|
209
178
|
|
|
210
|
-
//
|
|
179
|
+
// Decode verified plaintext results
|
|
211
180
|
(uint64 revealedAmount, uint64 winnerIdx) = abi.decode(
|
|
212
181
|
abiEncodedResults,
|
|
213
182
|
(uint64, uint64)
|
|
214
183
|
);
|
|
215
184
|
|
|
216
|
-
// 🏆 Look up winner address
|
|
217
185
|
if (revealedAmount >= minimumBid && winnerIdx < bidders.length) {
|
|
218
186
|
winner = bidders[winnerIdx];
|
|
219
187
|
winningAmount = revealedAmount;
|
|
220
188
|
} else {
|
|
221
|
-
// No valid winner (all bids below minimum)
|
|
222
189
|
winner = address(0);
|
|
223
190
|
winningAmount = 0;
|
|
224
191
|
}
|
|
@@ -228,8 +195,6 @@ contract BlindAuction is ZamaEthereumConfig {
|
|
|
228
195
|
emit WinnerRevealed(winner, winningAmount);
|
|
229
196
|
}
|
|
230
197
|
|
|
231
|
-
// ==================== VIEW FUNCTIONS ====================
|
|
232
|
-
|
|
233
198
|
/// @notice Get the number of bidders
|
|
234
199
|
function getBidderCount() external view returns (uint256) {
|
|
235
200
|
return bidders.length;
|
|
@@ -10,26 +10,16 @@ import {
|
|
|
10
10
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* @notice
|
|
13
|
+
* @notice Confidential escrow service with hidden transaction amounts.
|
|
14
|
+
* Buyer and seller agree on encrypted escrow amount. Funds are held
|
|
15
|
+
* securely until conditions are met. Amount remains hidden from public
|
|
16
|
+
* view until release or refund. Includes arbiter for dispute resolution.
|
|
17
|
+
* Perfect for high-value transactions requiring privacy.
|
|
14
18
|
*
|
|
15
|
-
* @dev
|
|
16
|
-
* -
|
|
17
|
-
* - Conditions verified without revealing values
|
|
18
|
-
* - Multi-party agreement pattern
|
|
19
|
-
* - Dispute resolution with arbiter
|
|
20
|
-
*
|
|
21
|
-
* Flow:
|
|
22
|
-
* 1. Buyer creates escrow with encrypted amount
|
|
23
|
-
* 2. Buyer deposits funds
|
|
24
|
-
* 3. Seller delivers goods/services
|
|
25
|
-
* 4. Buyer releases funds OR disputes
|
|
26
|
-
* 5. Arbiter resolves disputes if needed
|
|
27
|
-
*
|
|
28
|
-
* ⚠️ IMPORTANT: Amount revealed only on release/refund
|
|
19
|
+
* @dev Flow: createEscrow() → fundEscrow() → release()/requestRefund()/raiseDispute()
|
|
20
|
+
* Multi-party agreement with arbiter for disputes.
|
|
29
21
|
*/
|
|
30
22
|
contract EncryptedEscrow is ZamaEthereumConfig {
|
|
31
|
-
// ==================== TYPES ====================
|
|
32
|
-
|
|
33
23
|
enum EscrowState {
|
|
34
24
|
Created, // Escrow created, awaiting deposit
|
|
35
25
|
Funded, // Funds deposited
|
|
@@ -49,8 +39,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
49
39
|
uint256 deadline;
|
|
50
40
|
}
|
|
51
41
|
|
|
52
|
-
// ==================== STATE ====================
|
|
53
|
-
|
|
54
42
|
/// Contract owner
|
|
55
43
|
address public owner;
|
|
56
44
|
|
|
@@ -63,8 +51,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
63
51
|
/// Arbiter fee percentage (default 1%)
|
|
64
52
|
uint256 public arbiterFeePercent;
|
|
65
53
|
|
|
66
|
-
// ==================== EVENTS ====================
|
|
67
|
-
|
|
68
54
|
/// @notice Emitted when escrow is created
|
|
69
55
|
/// @param escrowId ID of the escrow
|
|
70
56
|
/// @param buyer Address of buyer
|
|
@@ -100,8 +86,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
100
86
|
/// @param winner Address favored in resolution
|
|
101
87
|
event DisputeResolved(uint256 indexed escrowId, address indexed winner);
|
|
102
88
|
|
|
103
|
-
// ==================== CONSTRUCTOR ====================
|
|
104
|
-
|
|
105
89
|
/// @notice Creates the escrow contract
|
|
106
90
|
/// @param _arbiterFeePercent Fee percentage for arbiter (0-10)
|
|
107
91
|
constructor(uint256 _arbiterFeePercent) {
|
|
@@ -110,8 +94,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
110
94
|
arbiterFeePercent = _arbiterFeePercent;
|
|
111
95
|
}
|
|
112
96
|
|
|
113
|
-
// ==================== ESCROW CREATION ====================
|
|
114
|
-
|
|
115
97
|
/// @notice Create a new escrow with encrypted amount
|
|
116
98
|
/// @param seller Address of the seller
|
|
117
99
|
/// @param arbiter Address of dispute arbiter
|
|
@@ -172,8 +154,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
172
154
|
emit EscrowFunded(escrowId, msg.value);
|
|
173
155
|
}
|
|
174
156
|
|
|
175
|
-
// ==================== RELEASE / REFUND ====================
|
|
176
|
-
|
|
177
157
|
/// @notice Release funds to seller
|
|
178
158
|
/// @dev Only buyer can release after delivery
|
|
179
159
|
/// @param escrowId ID of escrow to release
|
|
@@ -211,8 +191,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
211
191
|
emit FundsRefunded(escrowId, escrow.buyer);
|
|
212
192
|
}
|
|
213
193
|
|
|
214
|
-
// ==================== DISPUTE RESOLUTION ====================
|
|
215
|
-
|
|
216
194
|
/// @notice Raise a dispute
|
|
217
195
|
/// @param escrowId ID of escrow
|
|
218
196
|
function raiseDispute(uint256 escrowId) external {
|
|
@@ -264,8 +242,6 @@ contract EncryptedEscrow is ZamaEthereumConfig {
|
|
|
264
242
|
emit DisputeResolved(escrowId, winner);
|
|
265
243
|
}
|
|
266
244
|
|
|
267
|
-
// ==================== VIEW FUNCTIONS ====================
|
|
268
|
-
|
|
269
245
|
/// @notice Get escrow details
|
|
270
246
|
function getEscrow(
|
|
271
247
|
uint256 escrowId
|
|
@@ -5,33 +5,22 @@ import {FHE, euint64, ebool, externalEuint8} from "@fhevm/solidity/lib/FHE.sol";
|
|
|
5
5
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @notice
|
|
8
|
+
* @notice Private voting system with homomorphic vote tallying.
|
|
9
|
+
* Voters submit encrypted ballots (Yes/No). Votes are tallied using
|
|
10
|
+
* homomorphic addition WITHOUT decrypting individual ballots. Only the
|
|
11
|
+
* final tally is revealed - individual votes remain private forever.
|
|
12
|
+
* Perfect for DAO governance, elections, or any scenario requiring ballot secrecy.
|
|
9
13
|
*
|
|
10
|
-
* @dev
|
|
11
|
-
*
|
|
12
|
-
* - Homomorphic addition for vote tallying
|
|
13
|
-
* - FHE.makePubliclyDecryptable() for revealing final results
|
|
14
|
-
* - Privacy: individual votes remain encrypted forever
|
|
15
|
-
*
|
|
16
|
-
* Flow:
|
|
17
|
-
* 1. Owner creates a proposal with voting duration
|
|
18
|
-
* 2. Eligible voters submit encrypted votes (0 or 1)
|
|
19
|
-
* 3. Votes are homomorphically added to running totals
|
|
20
|
-
* 4. After voting ends, owner closes and results become decryptable
|
|
21
|
-
* 5. Anyone can reveal final Yes/No counts with valid proof
|
|
14
|
+
* @dev Flow: vote() → closeVoting() → revealResults()
|
|
15
|
+
* ⚡ Gas: Each vote costs ~200k gas (FHE.add + FHE.select operations)
|
|
22
16
|
*/
|
|
23
17
|
contract HiddenVoting is ZamaEthereumConfig {
|
|
24
|
-
// ==================== TYPES ====================
|
|
25
|
-
|
|
26
18
|
enum VotingState {
|
|
27
19
|
Active, // Accepting votes
|
|
28
20
|
Closed, // Voting ended, pending reveal
|
|
29
21
|
Revealed // Results revealed on-chain
|
|
30
22
|
}
|
|
31
23
|
|
|
32
|
-
// ==================== STATE ====================
|
|
33
|
-
|
|
34
|
-
/// Voting owner (deployer)
|
|
35
24
|
address public owner;
|
|
36
25
|
|
|
37
26
|
/// Current voting state
|
|
@@ -59,9 +48,7 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
59
48
|
uint64 public revealedYesVotes;
|
|
60
49
|
uint64 public revealedNoVotes;
|
|
61
50
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
/// @notice Emitted when a vote is cast
|
|
51
|
+
/// Emitted when a vote is cast
|
|
65
52
|
/// @param voter Address of the voter
|
|
66
53
|
event VoteCast(address indexed voter);
|
|
67
54
|
|
|
@@ -75,19 +62,11 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
75
62
|
/// @param noVotes Final No vote count
|
|
76
63
|
event ResultsRevealed(uint64 yesVotes, uint64 noVotes);
|
|
77
64
|
|
|
78
|
-
// ==================== MODIFIERS ====================
|
|
79
|
-
|
|
80
|
-
/// @dev Restricts function to owner only
|
|
81
65
|
modifier onlyOwner() {
|
|
82
66
|
require(msg.sender == owner, "Only owner can call");
|
|
83
67
|
_;
|
|
84
68
|
}
|
|
85
69
|
|
|
86
|
-
// ==================== CONSTRUCTOR ====================
|
|
87
|
-
|
|
88
|
-
/// @notice Creates a new voting proposal
|
|
89
|
-
/// @param _proposal Description of what is being voted on
|
|
90
|
-
/// @param _durationSeconds How long voting is open
|
|
91
70
|
constructor(string memory _proposal, uint256 _durationSeconds) {
|
|
92
71
|
require(bytes(_proposal).length > 0, "Empty proposal");
|
|
93
72
|
require(_durationSeconds > 0, "Duration must be positive");
|
|
@@ -97,18 +76,15 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
97
76
|
endTime = block.timestamp + _durationSeconds;
|
|
98
77
|
votingState = VotingState.Active;
|
|
99
78
|
|
|
100
|
-
//
|
|
79
|
+
// Initialize encrypted counters
|
|
101
80
|
_yesVotes = FHE.asEuint64(0);
|
|
102
81
|
_noVotes = FHE.asEuint64(0);
|
|
103
82
|
FHE.allowThis(_yesVotes);
|
|
104
83
|
FHE.allowThis(_noVotes);
|
|
105
84
|
}
|
|
106
85
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
/// @notice Cast an encrypted vote
|
|
110
|
-
/// @dev Vote must be 0 (No) or 1 (Yes). Values > 1 are treated as Yes.
|
|
111
|
-
/// @param encryptedVote Encrypted vote (0 or 1)
|
|
86
|
+
/// @notice Cast an encrypted vote (0=No, 1=Yes)
|
|
87
|
+
/// @dev Homomorphic tallying: votes added without decryption!
|
|
112
88
|
/// @param inputProof Proof validating the encrypted input
|
|
113
89
|
function vote(
|
|
114
90
|
externalEuint8 encryptedVote,
|
|
@@ -118,14 +94,13 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
118
94
|
require(block.timestamp < endTime, "Voting has ended");
|
|
119
95
|
require(!hasVoted[msg.sender], "Already voted");
|
|
120
96
|
|
|
121
|
-
//
|
|
122
|
-
// Note: Using euint8 for vote, but storing as euint64 for addition
|
|
97
|
+
// Convert vote and normalize to 0 or 1
|
|
123
98
|
euint64 voteValue = FHE.asEuint64(
|
|
124
99
|
FHE.fromExternal(encryptedVote, inputProof)
|
|
125
100
|
);
|
|
126
101
|
|
|
127
|
-
//
|
|
128
|
-
//
|
|
102
|
+
// 🧮 Why homomorphic? Votes are tallied WITHOUT decrypting individual ballots!
|
|
103
|
+
// Individual votes remain private forever
|
|
129
104
|
ebool isYes = FHE.gt(voteValue, FHE.asEuint64(0));
|
|
130
105
|
|
|
131
106
|
// ➕ Add to Yes counter if vote is Yes (1)
|
|
@@ -136,7 +111,6 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
136
111
|
);
|
|
137
112
|
_yesVotes = FHE.add(_yesVotes, yesIncrement);
|
|
138
113
|
|
|
139
|
-
// ➕ Add to No counter if vote is No (0)
|
|
140
114
|
euint64 noIncrement = FHE.select(
|
|
141
115
|
isYes,
|
|
142
116
|
FHE.asEuint64(0),
|
|
@@ -144,26 +118,21 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
144
118
|
);
|
|
145
119
|
_noVotes = FHE.add(_noVotes, noIncrement);
|
|
146
120
|
|
|
147
|
-
// ✅ Update permissions for new values
|
|
148
|
-
FHE.allowThis(_yesVotes);
|
|
149
121
|
FHE.allowThis(_noVotes);
|
|
150
122
|
|
|
151
|
-
// 📋 Record that this address has voted
|
|
152
123
|
hasVoted[msg.sender] = true;
|
|
153
124
|
voterCount++;
|
|
154
125
|
|
|
155
126
|
emit VoteCast(msg.sender);
|
|
156
127
|
}
|
|
157
128
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
/// @notice Close voting and prepare results for decryption
|
|
129
|
+
/// @notice Close voting and mark results for decryption
|
|
161
130
|
/// @dev Only owner can call after voting period ends
|
|
162
131
|
function closeVoting() external onlyOwner {
|
|
163
132
|
require(votingState == VotingState.Active, "Voting not active");
|
|
164
133
|
require(block.timestamp >= endTime, "Voting not yet ended");
|
|
165
134
|
|
|
166
|
-
//
|
|
135
|
+
// Mark totals for public decryption via relayer
|
|
167
136
|
FHE.makePubliclyDecryptable(_yesVotes);
|
|
168
137
|
FHE.makePubliclyDecryptable(_noVotes);
|
|
169
138
|
|
|
@@ -182,15 +151,15 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
182
151
|
) external {
|
|
183
152
|
require(votingState == VotingState.Closed, "Voting not closed");
|
|
184
153
|
|
|
185
|
-
//
|
|
154
|
+
// Build handle array for signature verification
|
|
186
155
|
bytes32[] memory cts = new bytes32[](2);
|
|
187
156
|
cts[0] = FHE.toBytes32(_yesVotes);
|
|
188
157
|
cts[1] = FHE.toBytes32(_noVotes);
|
|
189
158
|
|
|
190
|
-
//
|
|
159
|
+
// Verify KMS proof (reverts if invalid)
|
|
191
160
|
FHE.checkSignatures(cts, abiEncodedResults, decryptionProof);
|
|
192
161
|
|
|
193
|
-
//
|
|
162
|
+
// Decode verified plaintext results
|
|
194
163
|
(uint64 yesCount, uint64 noCount) = abi.decode(
|
|
195
164
|
abiEncodedResults,
|
|
196
165
|
(uint64, uint64)
|
|
@@ -203,15 +172,11 @@ contract HiddenVoting is ZamaEthereumConfig {
|
|
|
203
172
|
emit ResultsRevealed(yesCount, noCount);
|
|
204
173
|
}
|
|
205
174
|
|
|
206
|
-
// ==================== VIEW FUNCTIONS ====================
|
|
207
|
-
|
|
208
|
-
/// @notice Get encrypted Yes votes handle (after voting closed)
|
|
209
175
|
function getEncryptedYesVotes() external view returns (euint64) {
|
|
210
176
|
require(votingState != VotingState.Active, "Voting still active");
|
|
211
177
|
return _yesVotes;
|
|
212
178
|
}
|
|
213
179
|
|
|
214
|
-
/// @notice Get encrypted No votes handle (after voting closed)
|
|
215
180
|
function getEncryptedNoVotes() external view returns (euint64) {
|
|
216
181
|
require(votingState != VotingState.Active, "Voting still active");
|
|
217
182
|
return _noVotes;
|
|
@@ -12,24 +12,18 @@ import {
|
|
|
12
12
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* @notice
|
|
15
|
+
* @notice Privacy-preserving identity verification using predicate proofs.
|
|
16
|
+
* Users submit encrypted personal data (age, country, credit score).
|
|
17
|
+
* Contract can verify predicates like "Is user 18+?" or "Good credit?"
|
|
18
|
+
* without learning the actual values. Returns encrypted booleans that
|
|
19
|
+
* prove compliance without revealing sensitive information. Revolutionary
|
|
20
|
+
* for KYC/AML compliance while preserving user privacy.
|
|
16
21
|
*
|
|
17
|
-
* @dev
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* - Only boolean results revealed: "Is 18+?", "Is from allowed country?"
|
|
21
|
-
* - Personal data never leaves encrypted form
|
|
22
|
-
*
|
|
23
|
-
* Verification types:
|
|
24
|
-
* 1. Age verification: isAbove18(), isAbove21()
|
|
25
|
-
* 2. Country check: isFromAllowedCountry()
|
|
26
|
-
* 3. Credit score: hasCreditScoreAbove(threshold)
|
|
27
|
-
*
|
|
28
|
-
* ⚠️ IMPORTANT: This is a demo - production KYC needs trusted attesters!
|
|
22
|
+
* @dev Flow: submitKYC() → verifyAge18()/verifyGoodCredit()/etc.
|
|
23
|
+
* Returns encrypted booleans: "Is 18+?", "Good credit?" without revealing actual values.
|
|
24
|
+
* ⚠️ Production KYC needs trusted attesters!
|
|
29
25
|
*/
|
|
30
26
|
contract PrivateKYC is ZamaEthereumConfig {
|
|
31
|
-
// ==================== TYPES ====================
|
|
32
|
-
|
|
33
27
|
struct Identity {
|
|
34
28
|
euint8 age; // 0-255 years
|
|
35
29
|
euint8 countryCode; // ISO 3166-1 numeric (1-255)
|
|
@@ -38,8 +32,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
38
32
|
uint256 verifiedAt; // Timestamp of verification
|
|
39
33
|
}
|
|
40
34
|
|
|
41
|
-
// ==================== STATE ====================
|
|
42
|
-
|
|
43
35
|
/// Contract owner (KYC admin)
|
|
44
36
|
address public owner;
|
|
45
37
|
|
|
@@ -56,8 +48,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
56
48
|
/// Minimum credit score for "good" rating
|
|
57
49
|
uint16 public constant MIN_CREDIT_GOOD = 700;
|
|
58
50
|
|
|
59
|
-
// ==================== EVENTS ====================
|
|
60
|
-
|
|
61
51
|
/// @notice Emitted when user submits KYC
|
|
62
52
|
/// @param user Address of user
|
|
63
53
|
event KYCSubmitted(address indexed user);
|
|
@@ -76,8 +66,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
76
66
|
/// @param minAge Minimum age required
|
|
77
67
|
event AgeVerificationRequested(address indexed user, uint8 minAge);
|
|
78
68
|
|
|
79
|
-
// ==================== MODIFIERS ====================
|
|
80
|
-
|
|
81
69
|
modifier onlyOwner() {
|
|
82
70
|
require(msg.sender == owner, "Only owner");
|
|
83
71
|
_;
|
|
@@ -88,8 +76,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
88
76
|
_;
|
|
89
77
|
}
|
|
90
78
|
|
|
91
|
-
// ==================== CONSTRUCTOR ====================
|
|
92
|
-
|
|
93
79
|
/// @notice Creates the KYC contract
|
|
94
80
|
/// @param _allowedCountryCodes Initial list of allowed country codes
|
|
95
81
|
constructor(uint8[] memory _allowedCountryCodes) {
|
|
@@ -101,8 +87,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
101
87
|
}
|
|
102
88
|
}
|
|
103
89
|
|
|
104
|
-
// ==================== KYC SUBMISSION ====================
|
|
105
|
-
|
|
106
90
|
/// @notice Submit encrypted KYC data
|
|
107
91
|
/// @param encAge Encrypted age
|
|
108
92
|
/// @param encCountry Encrypted country code
|
|
@@ -147,8 +131,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
147
131
|
emit KYCRevoked(msg.sender);
|
|
148
132
|
}
|
|
149
133
|
|
|
150
|
-
// ==================== VERIFICATION FUNCTIONS ====================
|
|
151
|
-
|
|
152
134
|
/// @notice Check if user is 18 or older
|
|
153
135
|
/// @param user Address to verify
|
|
154
136
|
/// @return result Encrypted boolean result
|
|
@@ -232,8 +214,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
232
214
|
return result;
|
|
233
215
|
}
|
|
234
216
|
|
|
235
|
-
// ==================== DECRYPTABLE VERIFICATION ====================
|
|
236
|
-
|
|
237
217
|
/// @notice Request age verification with public result
|
|
238
218
|
/// @dev Makes the result publicly decryptable
|
|
239
219
|
/// @param user Address to verify
|
|
@@ -252,8 +232,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
252
232
|
return result;
|
|
253
233
|
}
|
|
254
234
|
|
|
255
|
-
// ==================== ADMIN FUNCTIONS ====================
|
|
256
|
-
|
|
257
235
|
/// @notice Update country allowlist
|
|
258
236
|
/// @param countryCode Country code to update
|
|
259
237
|
/// @param allowed Whether to allow or deny
|
|
@@ -278,8 +256,6 @@ contract PrivateKYC is ZamaEthereumConfig {
|
|
|
278
256
|
}
|
|
279
257
|
}
|
|
280
258
|
|
|
281
|
-
// ==================== VIEW FUNCTIONS ====================
|
|
282
|
-
|
|
283
259
|
/// @notice Check if user has submitted KYC
|
|
284
260
|
function hasSubmittedKYC(address user) external view returns (bool) {
|
|
285
261
|
return _identities[user].isVerified;
|