create-fhevm-example 1.4.2 → 1.4.4
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 +18 -57
- package/contracts/advanced/EncryptedEscrow.sol +2 -30
- package/contracts/advanced/HiddenVoting.sol +15 -54
- package/contracts/advanced/PrivateKYC.sol +3 -32
- package/contracts/advanced/PrivatePayroll.sol +4 -34
- package/contracts/basic/decryption/PublicDecryptMultipleValues.sol +9 -17
- package/contracts/basic/decryption/PublicDecryptSingleValue.sol +9 -16
- package/contracts/basic/decryption/UserDecryptMultipleValues.sol +7 -21
- package/contracts/basic/decryption/UserDecryptSingleValue.sol +11 -30
- package/contracts/basic/encryption/EncryptMultipleValues.sol +12 -21
- package/contracts/basic/encryption/EncryptSingleValue.sol +11 -17
- package/contracts/basic/encryption/FHECounter.sol +15 -16
- package/contracts/basic/fhe-operations/FHEAdd.sol +10 -16
- package/contracts/basic/fhe-operations/FHEArithmetic.sol +15 -31
- package/contracts/basic/fhe-operations/FHEComparison.sol +8 -29
- package/contracts/basic/fhe-operations/FHEIfThenElse.sol +4 -9
- package/contracts/concepts/FHEAccessControl.sol +23 -28
- package/contracts/concepts/FHEAntiPatterns.sol +4 -37
- package/contracts/concepts/FHEHandles.sol +10 -29
- package/contracts/concepts/FHEInputProof.sol +9 -33
- package/contracts/gaming/EncryptedLottery.sol +6 -37
- package/contracts/gaming/EncryptedPoker.sol +3 -33
- package/contracts/gaming/RockPaperScissors.sol +39 -64
- package/contracts/openzeppelin/VestingWallet.sol +8 -17
- package/dist/scripts/shared/generators.d.ts +12 -0
- package/dist/scripts/shared/generators.d.ts.map +1 -1
- package/dist/scripts/shared/generators.js +126 -2
- package/package.json +1 -1
|
@@ -5,14 +5,12 @@ import {FHE, euint8} from "@fhevm/solidity/lib/FHE.sol";
|
|
|
5
5
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @notice
|
|
8
|
+
* @notice 8-sided die roll game demonstrating public decryption of multiple encrypted values.
|
|
9
9
|
*
|
|
10
|
-
* @dev Uses FHE.
|
|
11
|
-
*
|
|
10
|
+
* @dev Uses FHE.randEuint8() + FHE.makePubliclyDecryptable() for both dice rolls.
|
|
11
|
+
* ⚠️ Order matters in cts[] array for checkSignatures!
|
|
12
12
|
*/
|
|
13
13
|
contract HighestDieRoll is ZamaEthereumConfig {
|
|
14
|
-
constructor() {}
|
|
15
|
-
|
|
16
14
|
// Simple counter to assign a unique ID to each new game.
|
|
17
15
|
uint256 private counter = 0;
|
|
18
16
|
|
|
@@ -68,7 +66,8 @@ contract HighestDieRoll is ZamaEthereumConfig {
|
|
|
68
66
|
revealed: false
|
|
69
67
|
});
|
|
70
68
|
|
|
71
|
-
//
|
|
69
|
+
// 🌐 Both values marked for public decryption
|
|
70
|
+
// Anyone can decrypt with valid KMS proof
|
|
72
71
|
FHE.makePubliclyDecryptable(playerAEncryptedDieRoll);
|
|
73
72
|
FHE.makePubliclyDecryptable(playerBEncryptedDieRoll);
|
|
74
73
|
|
|
@@ -119,13 +118,7 @@ contract HighestDieRoll is ZamaEthereumConfig {
|
|
|
119
118
|
) public {
|
|
120
119
|
require(!games[gameId].revealed, "Game already revealed");
|
|
121
120
|
|
|
122
|
-
//
|
|
123
|
-
// The verification checks that 'abiEncodedClearGameResult' is the true decryption
|
|
124
|
-
// of the '(playerAEncryptedDieRoll, playerBEncryptedDieRoll)' handle pair using
|
|
125
|
-
// the provided 'decryptionProof'.
|
|
126
|
-
|
|
127
|
-
// Creating the list of handles in the right order! In this case the order does not matter since the proof
|
|
128
|
-
// only involves 1 single handle.
|
|
121
|
+
// Verify KMS proof - ORDER MATTERS!
|
|
129
122
|
bytes32[] memory cts = new bytes32[](2);
|
|
130
123
|
cts[0] = FHE.toBytes32(games[gameId].playerAEncryptedDieRoll);
|
|
131
124
|
cts[1] = FHE.toBytes32(games[gameId].playerBEncryptedDieRoll);
|
|
@@ -133,9 +126,8 @@ contract HighestDieRoll is ZamaEthereumConfig {
|
|
|
133
126
|
// This FHE call reverts the transaction if the decryption proof is invalid.
|
|
134
127
|
FHE.checkSignatures(cts, abiEncodedClearGameResult, decryptionProof);
|
|
135
128
|
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
// `uint8` instead of an abi-encoded uint8 pair. In this case, we should have to compute abi.encode on-chain
|
|
129
|
+
// Decode both decrypted die rolls
|
|
130
|
+
// Note: Using abi.decode here, but could also accept two uint8 parameters
|
|
139
131
|
(
|
|
140
132
|
uint8 decodedClearPlayerADieRoll,
|
|
141
133
|
uint8 decodedClearPlayerBDieRoll
|
|
@@ -153,7 +145,7 @@ contract HighestDieRoll is ZamaEthereumConfig {
|
|
|
153
145
|
: address(0)
|
|
154
146
|
);
|
|
155
147
|
|
|
156
|
-
//
|
|
148
|
+
// Store game result
|
|
157
149
|
games[gameId].revealed = true;
|
|
158
150
|
games[gameId].winner = winner;
|
|
159
151
|
}
|
|
@@ -5,14 +5,12 @@ import {FHE, ebool} from "@fhevm/solidity/lib/FHE.sol";
|
|
|
5
5
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @notice
|
|
8
|
+
* @notice Simple Heads or Tails game demonstrating public, permissionless decryption with FHE.
|
|
9
9
|
*
|
|
10
|
-
* @dev Uses FHE.
|
|
11
|
-
*
|
|
10
|
+
* @dev Uses FHE.randEbool() for random result + FHE.makePubliclyDecryptable() for revealing.
|
|
11
|
+
* Anyone can decrypt results with valid KMS proof.
|
|
12
12
|
*/
|
|
13
13
|
contract HeadsOrTails is ZamaEthereumConfig {
|
|
14
|
-
constructor() {}
|
|
15
|
-
|
|
16
14
|
/// Simple counter to assign a unique ID to each new game.
|
|
17
15
|
uint256 private counter = 0;
|
|
18
16
|
|
|
@@ -66,7 +64,8 @@ contract HeadsOrTails is ZamaEthereumConfig {
|
|
|
66
64
|
winner: address(0)
|
|
67
65
|
});
|
|
68
66
|
|
|
69
|
-
//
|
|
67
|
+
// 🌐 Why makePubliclyDecryptable? Allows ANYONE to decrypt (not just allowed users)
|
|
68
|
+
// Use case: Public game results, lottery winners, voting tallies
|
|
70
69
|
FHE.makePubliclyDecryptable(headsOrTailsResult);
|
|
71
70
|
|
|
72
71
|
// You can catch the event to get the gameId and the encryptedHasHeadsWon handle
|
|
@@ -113,21 +112,15 @@ contract HeadsOrTails is ZamaEthereumConfig {
|
|
|
113
112
|
"Game winner already revealed"
|
|
114
113
|
);
|
|
115
114
|
|
|
116
|
-
//
|
|
117
|
-
// The verification checks that 'abiEncodedClearGameResult' is the true decryption
|
|
118
|
-
// of the 'encryptedHasHeadsWon' handle using the provided 'decryptionProof'.
|
|
119
|
-
|
|
120
|
-
// Creating the list of handles in the right order! In this case the order does not matter since the proof
|
|
121
|
-
// only involves 1 single handle.
|
|
115
|
+
// Verify KMS decryption proof
|
|
122
116
|
bytes32[] memory cts = new bytes32[](1);
|
|
123
117
|
cts[0] = FHE.toBytes32(games[gameId].encryptedHasHeadsWon);
|
|
124
118
|
|
|
125
119
|
// This FHE call reverts the transaction if the decryption proof is invalid.
|
|
126
120
|
FHE.checkSignatures(cts, abiEncodedClearGameResult, decryptionProof);
|
|
127
121
|
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
// `bool` instead of an abi-encoded bool. In this case, we should have compute abi.encode on-chain
|
|
122
|
+
// Decode the decrypted result to determine winner
|
|
123
|
+
// Note: Using abi.decode here, but could also accept plain bool parameter
|
|
131
124
|
bool decodedClearGameResult = abi.decode(
|
|
132
125
|
abiEncodedClearGameResult,
|
|
133
126
|
(bool)
|
|
@@ -136,7 +129,7 @@ contract HeadsOrTails is ZamaEthereumConfig {
|
|
|
136
129
|
? games[gameId].headsPlayer
|
|
137
130
|
: games[gameId].tailsPlayer;
|
|
138
131
|
|
|
139
|
-
//
|
|
132
|
+
// Store the winner
|
|
140
133
|
games[gameId].winner = winner;
|
|
141
134
|
}
|
|
142
135
|
}
|
|
@@ -5,33 +5,25 @@ import {FHE, ebool, euint32, euint64} from "@fhevm/solidity/lib/FHE.sol";
|
|
|
5
5
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @notice Demonstrates user decryption of multiple encrypted values
|
|
8
|
+
* @notice Demonstrates user decryption of multiple encrypted values with different types.
|
|
9
9
|
*
|
|
10
|
-
* @dev
|
|
11
|
-
*
|
|
10
|
+
* @dev Each value needs separate permission grants (no batching).
|
|
11
|
+
* ⚠️ Cannot batch permission grants - must call allow() for each value!
|
|
12
12
|
*/
|
|
13
13
|
contract UserDecryptMultipleValues is ZamaEthereumConfig {
|
|
14
|
-
// 🔐 Multiple encrypted values of different types
|
|
15
14
|
ebool private _encryptedBool;
|
|
16
15
|
euint32 private _encryptedUint32;
|
|
17
16
|
euint64 private _encryptedUint64;
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/// @notice Initialize multiple encrypted values from plaintext
|
|
22
|
-
/// @dev Uses FHE.asEuintX() to create encrypted constants from plaintext
|
|
23
|
-
/// (The plaintext IS visible on-chain, but result is encrypted)
|
|
18
|
+
/// @notice Initialize multiple values from plaintext
|
|
19
|
+
/// @dev FHE.asEuintX() creates encrypted constants from plaintext
|
|
24
20
|
function initialize(bool a, uint32 b, uint64 c) external {
|
|
25
|
-
// Create encrypted values from plaintext
|
|
26
|
-
// FHE.asEbool(a) encrypts a boolean value
|
|
21
|
+
// Create encrypted values from plaintext
|
|
27
22
|
_encryptedBool = FHE.xor(FHE.asEbool(a), FHE.asEbool(false));
|
|
28
|
-
|
|
29
|
-
// FHE.asEuint32(b) + 1 creates an encrypted (b + 1)
|
|
30
23
|
_encryptedUint32 = FHE.add(FHE.asEuint32(b), FHE.asEuint32(1));
|
|
31
24
|
_encryptedUint64 = FHE.add(FHE.asEuint64(c), FHE.asEuint64(1));
|
|
32
25
|
|
|
33
|
-
// ⚠️
|
|
34
|
-
// You cannot batch permission grants!
|
|
26
|
+
// ⚠️ Why separate? No batching for permissions - each needs individual allow()
|
|
35
27
|
FHE.allowThis(_encryptedBool);
|
|
36
28
|
FHE.allowThis(_encryptedUint32);
|
|
37
29
|
FHE.allowThis(_encryptedUint64);
|
|
@@ -41,20 +33,14 @@ contract UserDecryptMultipleValues is ZamaEthereumConfig {
|
|
|
41
33
|
FHE.allow(_encryptedUint64, msg.sender);
|
|
42
34
|
}
|
|
43
35
|
|
|
44
|
-
/// @notice Returns the encrypted boolean value
|
|
45
|
-
/// @dev Client decrypts with: fhevm.userDecrypt(FhevmType.ebool, handle, ...)
|
|
46
36
|
function encryptedBool() public view returns (ebool) {
|
|
47
37
|
return _encryptedBool;
|
|
48
38
|
}
|
|
49
39
|
|
|
50
|
-
/// @notice Returns the encrypted uint32 value
|
|
51
|
-
/// @dev Client decrypts with: fhevm.userDecrypt(FhevmType.euint32, handle, ...)
|
|
52
40
|
function encryptedUint32() public view returns (euint32) {
|
|
53
41
|
return _encryptedUint32;
|
|
54
42
|
}
|
|
55
43
|
|
|
56
|
-
/// @notice Returns the encrypted uint64 value
|
|
57
|
-
/// @dev Client decrypts with: fhevm.userDecrypt(FhevmType.euint64, handle, ...)
|
|
58
44
|
function encryptedUint64() public view returns (euint64) {
|
|
59
45
|
return _encryptedUint64;
|
|
60
46
|
}
|
|
@@ -5,54 +5,35 @@ import {FHE, euint32} from "@fhevm/solidity/lib/FHE.sol";
|
|
|
5
5
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @notice Demonstrates
|
|
8
|
+
* @notice Demonstrates FHE decryption mechanism and highlights the critical permission pattern.
|
|
9
9
|
*
|
|
10
|
-
* @dev
|
|
11
|
-
*
|
|
10
|
+
* @dev Shows CORRECT vs INCORRECT permission granting.
|
|
11
|
+
* ⚠️ Both allowThis + allow required for user decryption!
|
|
12
12
|
*/
|
|
13
13
|
contract UserDecryptSingleValue is ZamaEthereumConfig {
|
|
14
14
|
euint32 private _trivialEuint32;
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
constructor() {}
|
|
18
|
-
|
|
19
|
-
/// @notice Initialize with a trivial formula (value + 1)
|
|
20
|
-
/// @dev Demonstrates correct permission granting
|
|
16
|
+
/// @notice ✅ CORRECT: Proper permission pattern
|
|
21
17
|
function initializeUint32(uint32 value) external {
|
|
22
|
-
// Compute a trivial FHE formula _trivialEuint32 = value + 1
|
|
23
18
|
_trivialEuint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
|
|
24
19
|
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
// also enables the caller to perform user decryption.
|
|
29
|
-
//
|
|
30
|
-
// Note: If you forget to call `FHE.allowThis(_trivialEuint32)`, the user will NOT be able
|
|
31
|
-
// to user decrypt the value! Both the contract and the caller must have FHE permissions
|
|
32
|
-
// for user decryption to succeed.
|
|
20
|
+
// 🔑 Why both needed?
|
|
21
|
+
// - allowThis: Contract authorizes releasing the value
|
|
22
|
+
// - allow: User can request decryption
|
|
33
23
|
FHE.allowThis(_trivialEuint32);
|
|
34
24
|
FHE.allow(_trivialEuint32, msg.sender);
|
|
35
25
|
}
|
|
36
26
|
|
|
37
|
-
/// @notice
|
|
38
|
-
/// @dev
|
|
27
|
+
/// @notice ❌ WRONG: Missing allowThis causes decryption to FAIL!
|
|
28
|
+
/// @dev Common mistake - user gets permission but decryption still fails
|
|
39
29
|
function initializeUint32Wrong(uint32 value) external {
|
|
40
|
-
// Compute a trivial FHE formula _trivialEuint32 = value + 1
|
|
41
30
|
_trivialEuint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
|
|
42
31
|
|
|
43
|
-
// ❌
|
|
44
|
-
//
|
|
45
|
-
// We grant FHE permissions to the contract caller (`msg.sender`),
|
|
46
|
-
// expecting they will be able to user decrypt the encrypted value later.
|
|
47
|
-
//
|
|
48
|
-
// However, this will fail! 💥
|
|
49
|
-
// The contract itself (`address(this)`) also needs FHE permissions to allow user decryption.
|
|
50
|
-
// Without granting the contract access using `FHE.allowThis(...)`,
|
|
51
|
-
// the user decryption attempt by the user will not succeed.
|
|
32
|
+
// ❌ Missing allowThis → user can't decrypt!
|
|
33
|
+
// Why? Decryption needs contract authorization to release
|
|
52
34
|
FHE.allow(_trivialEuint32, msg.sender);
|
|
53
35
|
}
|
|
54
36
|
|
|
55
|
-
/// @notice Returns the encrypted uint32 value
|
|
56
37
|
function encryptedUint32() public view returns (euint32) {
|
|
57
38
|
return _trivialEuint32;
|
|
58
39
|
}
|
|
@@ -15,39 +15,30 @@ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
|
15
15
|
/**
|
|
16
16
|
* @notice Encrypting and handling multiple values in a single transaction efficiently.
|
|
17
17
|
*
|
|
18
|
-
* @dev
|
|
19
|
-
*
|
|
20
|
-
* - ebool: encrypted boolean
|
|
21
|
-
* - eaddress: encrypted address
|
|
22
|
-
* - ebytes64/128/256: encrypted bytes
|
|
18
|
+
* @dev Demonstrates batched input (ONE proof for multiple values).
|
|
19
|
+
* ⚡ Gas: Batching saves ~50k gas vs separate proofs!
|
|
23
20
|
*/
|
|
24
21
|
contract EncryptMultipleValues is ZamaEthereumConfig {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
eaddress private _encryptedEaddress; // e.g., hidden recipient
|
|
22
|
+
ebool private _encryptedEbool;
|
|
23
|
+
euint32 private _encryptedEuint32;
|
|
24
|
+
eaddress private _encryptedEaddress;
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
///
|
|
33
|
-
/// @dev Client-side batching example:
|
|
34
|
-
/// const input = await fhevm.createEncryptedInput(contractAddr, userAddr)
|
|
35
|
-
/// .addBool(true).add32(123).addAddress(addr).encrypt();
|
|
36
|
-
/// // This creates ONE proof for ALL values - more gas efficient!
|
|
26
|
+
/// @notice Store multiple encrypted values from single batched input
|
|
27
|
+
/// @dev Client creates ONE proof for ALL values using createEncryptedInput().
|
|
28
|
+
/// Much cheaper than separate encrypt() calls!
|
|
37
29
|
function initialize(
|
|
38
30
|
externalEbool inputEbool,
|
|
39
31
|
externalEuint32 inputEuint32,
|
|
40
32
|
externalEaddress inputEaddress,
|
|
41
|
-
bytes calldata inputProof // Single proof covers all
|
|
33
|
+
bytes calldata inputProof // Single proof covers all!
|
|
42
34
|
) external {
|
|
43
|
-
//
|
|
35
|
+
// 💡 Why one proof? Client batches all values before encrypt()
|
|
36
|
+
// Saves ~50k gas per additional value!
|
|
44
37
|
_encryptedEbool = FHE.fromExternal(inputEbool, inputProof);
|
|
45
38
|
_encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
|
|
46
39
|
_encryptedEaddress = FHE.fromExternal(inputEaddress, inputProof);
|
|
47
40
|
|
|
48
|
-
//
|
|
49
|
-
// You cannot batch permission grants
|
|
50
|
-
|
|
41
|
+
// 🔐 Each value needs own permissions (no batching here)
|
|
51
42
|
FHE.allowThis(_encryptedEbool);
|
|
52
43
|
FHE.allow(_encryptedEbool, msg.sender);
|
|
53
44
|
|
|
@@ -5,39 +5,33 @@ import {FHE, externalEuint32, euint32} from "@fhevm/solidity/lib/FHE.sol";
|
|
|
5
5
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @notice FHE encryption mechanism with single values, including common pitfalls and best practices
|
|
9
|
-
|
|
8
|
+
* @notice FHE encryption mechanism with single values, including common pitfalls and best practices.
|
|
9
|
+
|
|
10
10
|
* @dev Shows the complete flow: receiving encrypted input from user, validating proof,
|
|
11
11
|
* storing the encrypted value, and granting permissions for decryption.
|
|
12
12
|
*/
|
|
13
13
|
contract EncryptSingleValue is ZamaEthereumConfig {
|
|
14
|
-
// 🔐 Stored encrypted - only authorized users can decrypt
|
|
15
14
|
euint32 private _encryptedEuint32;
|
|
16
15
|
|
|
17
|
-
constructor() {}
|
|
18
|
-
|
|
19
16
|
/// @notice Store an encrypted value submitted by the user
|
|
20
|
-
/// @dev
|
|
21
|
-
///
|
|
17
|
+
/// @dev inputProof ensures: value encrypted for THIS contract + THIS user.
|
|
18
|
+
/// Prevents replay attacks from other contracts/users.
|
|
22
19
|
function initialize(
|
|
23
20
|
externalEuint32 inputEuint32,
|
|
24
21
|
bytes calldata inputProof
|
|
25
22
|
) external {
|
|
26
|
-
//
|
|
27
|
-
// 📋 The proof ensures:
|
|
28
|
-
// - Value was encrypted for THIS contract address
|
|
29
|
-
// - Value was encrypted by THIS user (msg.sender)
|
|
30
|
-
// - Prevents replay attacks from other contracts/users
|
|
23
|
+
// 🔐 Why proof? Prevents: replay attacks, wrong contract, invalid ciphertext
|
|
31
24
|
_encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
|
|
32
25
|
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
FHE.
|
|
26
|
+
// 🔑 Why both?
|
|
27
|
+
// - allowThis: Contract can store/compute with it
|
|
28
|
+
// - allow(user): User can decrypt it
|
|
29
|
+
FHE.allowThis(_encryptedEuint32);
|
|
30
|
+
FHE.allow(_encryptedEuint32, msg.sender);
|
|
37
31
|
}
|
|
38
32
|
|
|
39
33
|
/// @notice Returns the encrypted handle (not the actual value!)
|
|
40
|
-
/// @dev To decrypt, use fhevm.
|
|
34
|
+
/// @dev To decrypt, use fhevm.userDecryptEuint32() on client side
|
|
41
35
|
function encryptedUint32() public view returns (euint32) {
|
|
42
36
|
return _encryptedEuint32;
|
|
43
37
|
}
|
|
@@ -6,46 +6,45 @@ import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @notice Confidential counter implementation using FHEVM, compared with a standard counter to highlight encryption benefits.
|
|
9
|
-
|
|
10
|
-
* @dev Demonstrates basic FHE
|
|
11
|
-
*
|
|
9
|
+
|
|
10
|
+
* @dev Demonstrates basic FHE workflow: fromExternal() → compute → allow permissions.
|
|
11
|
+
* All arithmetic happens on encrypted values without revealing the count.
|
|
12
12
|
*/
|
|
13
13
|
contract FHECounter is ZamaEthereumConfig {
|
|
14
|
-
// 🔐 The count is always encrypted - no one can see the actual value
|
|
15
14
|
euint32 private _count;
|
|
16
15
|
|
|
17
|
-
/// @notice Returns the encrypted count handle (not the actual value!)
|
|
18
16
|
function getCount() external view returns (euint32) {
|
|
19
17
|
return _count;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
/// @notice Increments
|
|
20
|
+
/// @notice Increments counter by encrypted amount
|
|
21
|
+
/// @dev Why allowThis + allow? Contract needs permission to store,
|
|
22
|
+
/// user needs permission to decrypt. Both required!
|
|
23
23
|
function increment(
|
|
24
24
|
externalEuint32 inputEuint32,
|
|
25
25
|
bytes calldata inputProof
|
|
26
26
|
) external {
|
|
27
|
-
//
|
|
28
|
-
// The proof is verified automatically
|
|
27
|
+
// 🔐 Why proof? Ensures valid ciphertext encrypted for THIS contract
|
|
29
28
|
euint32 encryptedValue = FHE.fromExternal(inputEuint32, inputProof);
|
|
30
29
|
|
|
31
|
-
//
|
|
32
|
-
// Neither the contract nor anyone else sees the actual numbers
|
|
30
|
+
// 🧮 Homomorphic add: works on encrypted data
|
|
33
31
|
_count = FHE.add(_count, encryptedValue);
|
|
34
32
|
|
|
35
|
-
//
|
|
36
|
-
FHE.allowThis(_count);
|
|
37
|
-
FHE.allow(_count, msg.sender);
|
|
33
|
+
// 🔑 Both needed: allowThis = contract stores, allow = user decrypts
|
|
34
|
+
FHE.allowThis(_count);
|
|
35
|
+
FHE.allow(_count, msg.sender);
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
/// @notice Decrements
|
|
38
|
+
/// @notice Decrements counter by encrypted amount
|
|
39
|
+
/// @dev ⚠️ No underflow protection! FHE.sub wraps around at 0.
|
|
40
|
+
/// ❌ WRONG: Checking result < 0 reveals information
|
|
41
|
+
/// ✅ CORRECT: Use application-level balance tracking or FHE.select()
|
|
41
42
|
function decrement(
|
|
42
43
|
externalEuint32 inputEuint32,
|
|
43
44
|
bytes calldata inputProof
|
|
44
45
|
) external {
|
|
45
46
|
euint32 encryptedValue = FHE.fromExternal(inputEuint32, inputProof);
|
|
46
47
|
|
|
47
|
-
// Subtract encrypted values
|
|
48
|
-
// ⚠️ No underflow protection here - add checks in production!
|
|
49
48
|
_count = FHE.sub(_count, encryptedValue);
|
|
50
49
|
|
|
51
50
|
FHE.allowThis(_count);
|
|
@@ -5,21 +5,19 @@ import {FHE, euint8, externalEuint8} from "@fhevm/solidity/lib/FHE.sol";
|
|
|
5
5
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @notice Simple example
|
|
9
|
-
|
|
10
|
-
* @dev
|
|
8
|
+
* @notice Simple example demonstrating addition of two encrypted values (a + b)
|
|
9
|
+
|
|
10
|
+
* @dev Shows the most basic FHE operation and permission flow.
|
|
11
|
+
* ⚡ Gas: FHE.add() costs ~100k gas (coprocessor call)
|
|
11
12
|
*/
|
|
12
13
|
contract FHEAdd is ZamaEthereumConfig {
|
|
13
14
|
euint8 private _a;
|
|
14
15
|
euint8 private _b;
|
|
15
16
|
euint8 private _result;
|
|
16
17
|
|
|
17
|
-
constructor() {}
|
|
18
|
-
|
|
19
18
|
/// @notice Set the first operand (encrypted)
|
|
20
19
|
function setA(externalEuint8 inputA, bytes calldata inputProof) external {
|
|
21
20
|
_a = FHE.fromExternal(inputA, inputProof);
|
|
22
|
-
// Only contract needs permission to use this for computation
|
|
23
21
|
FHE.allowThis(_a);
|
|
24
22
|
}
|
|
25
23
|
|
|
@@ -30,21 +28,17 @@ contract FHEAdd is ZamaEthereumConfig {
|
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
/// @notice Compute a + b on encrypted values
|
|
33
|
-
/// @dev
|
|
31
|
+
/// @dev Contract operates on ciphertexts - never sees actual values!
|
|
34
32
|
function computeAPlusB() external {
|
|
35
|
-
//
|
|
36
|
-
// Neither the contract nor anyone else knows what a, b, or result are
|
|
33
|
+
// 🧮 Homomorphic: operates on encrypted data without decrypting
|
|
37
34
|
_result = FHE.add(_a, _b);
|
|
38
35
|
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// We need PERMANENT permissions for future access:
|
|
43
|
-
FHE.allowThis(_result); // Contract can use result later
|
|
44
|
-
FHE.allow(_result, msg.sender); // Caller can decrypt result
|
|
36
|
+
// 🔑 Why both? allowThis = contract can use it, allow = user can decrypt
|
|
37
|
+
FHE.allowThis(_result);
|
|
38
|
+
FHE.allow(_result, msg.sender);
|
|
45
39
|
}
|
|
46
40
|
|
|
47
|
-
/// @notice Returns the encrypted result
|
|
41
|
+
/// @notice Returns the encrypted result handle
|
|
48
42
|
function result() public view returns (euint8) {
|
|
49
43
|
return _result;
|
|
50
44
|
}
|
|
@@ -5,93 +5,77 @@ import {FHE, euint32, externalEuint32} from "@fhevm/solidity/lib/FHE.sol";
|
|
|
5
5
|
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @notice Demonstrates all FHE arithmetic operations
|
|
8
|
+
* @notice Demonstrates all FHE arithmetic operations: add, sub, mul, div, rem, min, max
|
|
9
9
|
*
|
|
10
|
-
* @dev
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Available operations:
|
|
14
|
-
* - FHE.add(a, b) : Addition
|
|
15
|
-
* - FHE.sub(a, b) : Subtraction
|
|
16
|
-
* - FHE.mul(a, b) : Multiplication
|
|
17
|
-
* - FHE.div(a, b) : Division (integer)
|
|
18
|
-
* - FHE.rem(a, b) : Remainder (modulo)
|
|
19
|
-
* - FHE.min(a, b) : Minimum of two values
|
|
20
|
-
* - FHE.max(a, b) : Maximum of two values
|
|
10
|
+
* @dev ⚡ Gas costs vary: add/sub (~100k) < mul (~150k) < div/rem (~300k)
|
|
11
|
+
* ⚠️ div/rem only work with plaintext divisor (not encrypted!)
|
|
21
12
|
*/
|
|
22
13
|
contract FHEArithmetic is ZamaEthereumConfig {
|
|
23
14
|
euint32 private _a;
|
|
24
15
|
euint32 private _b;
|
|
25
16
|
euint32 private _result;
|
|
26
17
|
|
|
27
|
-
// solhint-disable-next-line no-empty-blocks
|
|
28
|
-
constructor() {}
|
|
29
|
-
|
|
30
|
-
/// @notice Sets the first operand (encrypted)
|
|
31
18
|
function setA(externalEuint32 inputA, bytes calldata inputProof) external {
|
|
32
19
|
_a = FHE.fromExternal(inputA, inputProof);
|
|
33
20
|
FHE.allowThis(_a);
|
|
34
21
|
}
|
|
35
22
|
|
|
36
|
-
/// @notice Sets the second operand (encrypted)
|
|
37
23
|
function setB(externalEuint32 inputB, bytes calldata inputProof) external {
|
|
38
24
|
_b = FHE.fromExternal(inputB, inputProof);
|
|
39
25
|
FHE.allowThis(_b);
|
|
40
26
|
}
|
|
41
27
|
|
|
42
|
-
/// @notice
|
|
28
|
+
/// @notice Encrypted addition: result = a + b
|
|
43
29
|
function computeAdd() external {
|
|
44
30
|
_result = FHE.add(_a, _b);
|
|
45
31
|
_grantPermissions();
|
|
46
32
|
}
|
|
47
33
|
|
|
48
|
-
/// @notice
|
|
49
|
-
/// @dev No underflow protection
|
|
34
|
+
/// @notice Encrypted subtraction: result = a - b
|
|
35
|
+
/// @dev ⚠️ No underflow protection! Wraps around at 0.
|
|
50
36
|
function computeSub() external {
|
|
51
37
|
_result = FHE.sub(_a, _b);
|
|
52
38
|
_grantPermissions();
|
|
53
39
|
}
|
|
54
40
|
|
|
55
|
-
/// @notice
|
|
56
|
-
/// @dev No overflow protection
|
|
41
|
+
/// @notice Encrypted multiplication: result = a * b
|
|
42
|
+
/// @dev ⚠️ No overflow protection! May wrap at type max.
|
|
57
43
|
function computeMul() external {
|
|
58
44
|
_result = FHE.mul(_a, _b);
|
|
59
45
|
_grantPermissions();
|
|
60
46
|
}
|
|
61
47
|
|
|
62
|
-
/// @notice
|
|
63
|
-
/// @dev
|
|
48
|
+
/// @notice Encrypted division: result = a / divisor
|
|
49
|
+
/// @dev ❌ WRONG: FHE.div(encryptedA, encryptedB) - not supported!
|
|
50
|
+
/// ✅ CORRECT: FHE.div(encryptedA, plaintextB)
|
|
64
51
|
function computeDiv(uint32 divisor) external {
|
|
65
52
|
_result = FHE.div(_a, divisor);
|
|
66
53
|
_grantPermissions();
|
|
67
54
|
}
|
|
68
55
|
|
|
69
|
-
/// @notice
|
|
70
|
-
/// @dev
|
|
56
|
+
/// @notice Encrypted remainder: result = a % modulus
|
|
57
|
+
/// @dev Modulus must be plaintext (same limitation as div)
|
|
71
58
|
function computeRem(uint32 modulus) external {
|
|
72
59
|
_result = FHE.rem(_a, modulus);
|
|
73
60
|
_grantPermissions();
|
|
74
61
|
}
|
|
75
62
|
|
|
76
|
-
/// @notice
|
|
63
|
+
/// @notice Encrypted minimum: result = min(a, b)
|
|
77
64
|
function computeMin() external {
|
|
78
65
|
_result = FHE.min(_a, _b);
|
|
79
66
|
_grantPermissions();
|
|
80
67
|
}
|
|
81
68
|
|
|
82
|
-
/// @notice
|
|
69
|
+
/// @notice Encrypted maximum: result = max(a, b)
|
|
83
70
|
function computeMax() external {
|
|
84
71
|
_result = FHE.max(_a, _b);
|
|
85
72
|
_grantPermissions();
|
|
86
73
|
}
|
|
87
74
|
|
|
88
|
-
/// @notice Returns the encrypted result
|
|
89
|
-
/// @dev Caller must have FHE permissions to decrypt
|
|
90
75
|
function getResult() public view returns (euint32) {
|
|
91
76
|
return _result;
|
|
92
77
|
}
|
|
93
78
|
|
|
94
|
-
/// @dev Grants FHE permissions to contract and caller for decryption
|
|
95
79
|
function _grantPermissions() internal {
|
|
96
80
|
FHE.allowThis(_result);
|
|
97
81
|
FHE.allow(_result, msg.sender);
|