@inco/lightning 0.8.0-devnet-3 → 0.8.0-devnet-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/README.md +59 -1
- package/manifest.yaml +22 -0
- package/package.json +1 -1
- package/src/DeployUtils.sol +71 -25
- package/src/IncoLightning.sol +27 -7
- package/src/IncoVerifier.sol +18 -1
- package/src/Lib.alphanet.sol +390 -3
- package/src/Lib.demonet.sol +390 -3
- package/src/Lib.devnet.sol +391 -4
- package/src/Lib.sol +391 -4
- package/src/Lib.template.sol +387 -0
- package/src/Lib.testnet.sol +390 -3
- package/src/libs/incoLightning_alphanet_v0_297966649.sol +390 -3
- package/src/libs/incoLightning_alphanet_v1_725458969.sol +390 -3
- package/src/libs/incoLightning_alphanet_v2_976644394.sol +390 -3
- package/src/libs/incoLightning_demonet_v0_863421733.sol +390 -3
- package/src/libs/incoLightning_demonet_v2_467437523.sol +390 -3
- package/src/libs/incoLightning_devnet_v0_340846814.sol +390 -3
- package/src/libs/incoLightning_devnet_v1_904635675.sol +390 -3
- package/src/libs/incoLightning_devnet_v2_295237520.sol +390 -3
- package/src/libs/incoLightning_devnet_v3_976859633.sol +390 -3
- package/src/libs/incoLightning_devnet_v4_409204766.sol +921 -0
- package/src/libs/incoLightning_testnet_v0_183408998.sol +390 -3
- package/src/libs/incoLightning_testnet_v2_889158349.sol +390 -3
- package/src/lightning-parts/AccessControl/AdvancedAccessControl.sol +65 -4
- package/src/lightning-parts/AccessControl/BaseAccessControlList.sol +71 -5
- package/src/lightning-parts/DecryptionAttester.sol +16 -3
- package/src/lightning-parts/EncryptedInput.sol +80 -17
- package/src/lightning-parts/EncryptedOperations.sol +134 -1
- package/src/lightning-parts/Fee.sol +29 -6
- package/src/lightning-parts/interfaces/IEncryptedInput.sol +3 -3
- package/src/lightning-parts/primitives/EventCounter.sol +36 -5
- package/src/lightning-parts/primitives/HandleGeneration.sol +49 -13
- package/src/lightning-parts/primitives/HandleMetadata.sol +28 -0
- package/src/lightning-parts/primitives/LightningAddressGetter.sol +10 -0
- package/src/lightning-parts/primitives/VerifierAddressGetter.sol +10 -0
- package/src/lightning-parts/primitives/test/SignatureVerifier.t.sol +0 -2
- package/src/lightning-parts/test/HandleMetadata.t.sol +21 -13
- package/src/periphery/IncoUtils.sol +1 -0
- package/src/periphery/SessionVerifier.sol +35 -7
- package/src/test/FakeIncoInfra/FakeIncoInfraBase.sol +10 -2
- package/src/test/FakeIncoInfra/MockOpHandler.sol +1 -1
- package/src/test/TestFakeInfra.t.sol +536 -1
- package/src/version/IncoLightningConfig.sol +2 -2
- package/src/libs/incoLightning_devnet_v1_887305889.sol +0 -453
- package/src/libs/incoLightning_testnet_v1_938327937.sol +0 -453
|
@@ -10,14 +10,24 @@ import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/Signa
|
|
|
10
10
|
import {IBaseAccessControlList} from "./interfaces/IBaseAccessControlList.sol";
|
|
11
11
|
import {LightningAddressGetter} from "../primitives/LightningAddressGetter.sol";
|
|
12
12
|
|
|
13
|
+
/// @title AdvancedAccessControlStorage
|
|
14
|
+
/// @notice Diamond storage pattern for advanced ACL session nonce tracking
|
|
15
|
+
/// @dev Stores per-account session nonces that allow users to invalidate all previously signed vouchers
|
|
13
16
|
abstract contract AdvancedAccessControlStorage {
|
|
14
17
|
|
|
18
|
+
/// @notice Storage struct for advanced access control state
|
|
15
19
|
struct AacStorage {
|
|
16
|
-
|
|
20
|
+
/// @notice Maps account addresses to their active voucher session nonce
|
|
21
|
+
/// @dev Vouchers signed with a different nonce are invalid. Initial nonce is bytes32(0).
|
|
22
|
+
mapping(address => bytes32) activeVouchersSessionNonce;
|
|
17
23
|
}
|
|
18
24
|
|
|
25
|
+
/// @notice Storage slot location using keccak256 of a unique namespace string
|
|
19
26
|
bytes32 private constant AAC_STORAGE_LOCATION = keccak256("inco.storage.AdvancedAccessControl");
|
|
20
27
|
|
|
28
|
+
/// @notice Retrieves the storage struct from its dedicated slot
|
|
29
|
+
/// @dev Uses assembly to directly access the storage slot for gas efficiency
|
|
30
|
+
/// @return $ Reference to the AacStorage struct
|
|
21
31
|
function getAacStorage() internal pure returns (AacStorage storage $) {
|
|
22
32
|
bytes32 loc = AAC_STORAGE_LOCATION;
|
|
23
33
|
assembly {
|
|
@@ -27,12 +37,24 @@ abstract contract AdvancedAccessControlStorage {
|
|
|
27
37
|
|
|
28
38
|
}
|
|
29
39
|
|
|
40
|
+
/// @title VoucherEip712Checker
|
|
41
|
+
/// @notice EIP-712 typed data signing utilities for allowance vouchers
|
|
42
|
+
/// @dev Provides deterministic hashing of AllowanceVoucher structs for signature verification.
|
|
43
|
+
/// The voucher structure allows sharers to delegate access to their encrypted data
|
|
44
|
+
/// with flexible verification logic defined by an external contract.
|
|
30
45
|
abstract contract VoucherEip712Checker is IVoucherEip712Checker, EIP712Upgradeable {
|
|
31
46
|
|
|
47
|
+
/// @notice EIP-712 type hash for the AllowanceVoucher struct
|
|
48
|
+
/// @dev Computed once at compile time for gas efficiency
|
|
32
49
|
bytes32 constant ALLOWANCE_VOUCHER_STRUCT_HASH = keccak256(
|
|
33
50
|
"AllowanceVoucher(bytes32 sessionNonce,address verifyingContract,bytes4 callFunction,bytes sharerArgData)"
|
|
34
51
|
);
|
|
35
52
|
|
|
53
|
+
/// @notice Computes the EIP-712 digest for an allowance voucher
|
|
54
|
+
/// @dev The digest can be signed by the sharer to authorize access.
|
|
55
|
+
/// Uses EIP-712 structured data hashing with domain separator.
|
|
56
|
+
/// @param voucher The voucher to compute the digest for
|
|
57
|
+
/// @return The EIP-712 typed data hash ready for signing
|
|
36
58
|
function allowanceVoucherDigest(AllowanceVoucher memory voucher) public view returns (bytes32) {
|
|
37
59
|
return _hashTypedDataV4(
|
|
38
60
|
keccak256(
|
|
@@ -49,6 +71,21 @@ abstract contract VoucherEip712Checker is IVoucherEip712Checker, EIP712Upgradeab
|
|
|
49
71
|
|
|
50
72
|
}
|
|
51
73
|
|
|
74
|
+
/// @title AdvancedAccessControl
|
|
75
|
+
/// @notice Voucher-based access delegation for encrypted data sharing
|
|
76
|
+
/// @dev Enables flexible access control patterns where data owners can sign vouchers
|
|
77
|
+
/// authorizing others to access their encrypted data under specific conditions.
|
|
78
|
+
///
|
|
79
|
+
/// Voucher flow:
|
|
80
|
+
/// 1. Sharer signs an AllowanceVoucher specifying conditions (via verifyingContract)
|
|
81
|
+
/// 2. Requester presents the voucher with proof when requesting access
|
|
82
|
+
/// 3. This contract verifies the signature and calls the verifyingContract
|
|
83
|
+
/// 4. Access is granted if the verifyingContract returns ALLOWANCE_GRANTED_MAGIC_VALUE
|
|
84
|
+
///
|
|
85
|
+
/// Security features:
|
|
86
|
+
/// - Session nonces allow sharers to invalidate all previous vouchers
|
|
87
|
+
/// - EIP-712 typed data ensures vouchers are human-readable when signing
|
|
88
|
+
/// - Supports both EOA and smart contract signatures (ERC-1271)
|
|
52
89
|
abstract contract AdvancedAccessControl is
|
|
53
90
|
IAdvancedAccessControl,
|
|
54
91
|
AdvancedAccessControlStorage,
|
|
@@ -58,11 +95,28 @@ abstract contract AdvancedAccessControl is
|
|
|
58
95
|
|
|
59
96
|
using SignatureChecker for address;
|
|
60
97
|
|
|
98
|
+
/// @notice Thrown when a voucher signature is invalid for the claimed signer
|
|
99
|
+
/// @param signer The address that allegedly signed the voucher
|
|
100
|
+
/// @param digest The EIP-712 digest that should have been signed
|
|
101
|
+
/// @param signature The invalid signature provided
|
|
61
102
|
error InvalidVoucherSignature(address signer, bytes32 digest, bytes signature);
|
|
103
|
+
|
|
104
|
+
/// @notice Thrown when a voucher's session nonce doesn't match the sharer's active nonce
|
|
105
|
+
/// @param providedSessionNonce The nonce in the voucher
|
|
106
|
+
/// @param activeSessionNonce The sharer's current active session nonce
|
|
62
107
|
error InvalidVoucherSessionNonce(bytes32 providedSessionNonce, bytes32 activeSessionNonce);
|
|
63
108
|
|
|
64
|
-
/// @
|
|
65
|
-
/// @dev
|
|
109
|
+
/// @notice Checks if an account is allowed to access a handle using a signed voucher proof
|
|
110
|
+
/// @dev Intended for simulation/off-chain calls. Not a view function as it calls external contracts.
|
|
111
|
+
/// Verification steps:
|
|
112
|
+
/// 1. Verify the sharer has access to the handle
|
|
113
|
+
/// 2. Verify the voucher signature is valid
|
|
114
|
+
/// 3. Verify the session nonce is current
|
|
115
|
+
/// 4. Call the verifyingContract to check access conditions
|
|
116
|
+
/// @param handle The encrypted value handle to check access for
|
|
117
|
+
/// @param account The account requesting access
|
|
118
|
+
/// @param proof The allowance proof containing voucher, signature, and requester data
|
|
119
|
+
/// @return True if access is allowed, false or reverts otherwise
|
|
66
120
|
function isAllowedWithProof(bytes32 handle, address account, AllowanceProof memory proof) public returns (bool) {
|
|
67
121
|
require(
|
|
68
122
|
IBaseAccessControlList(incoLightningAddress).isAllowed(handle, proof.sharer),
|
|
@@ -87,11 +141,18 @@ abstract contract AdvancedAccessControl is
|
|
|
87
141
|
return (success && result.length >= 32 && abi.decode(result, (bytes32)) == ALLOWANCE_GRANTED_MAGIC_VALUE);
|
|
88
142
|
}
|
|
89
143
|
|
|
144
|
+
/// @notice Returns the current active voucher session nonce for an account
|
|
145
|
+
/// @dev Vouchers must include this nonce to be valid. Initial value is bytes32(0).
|
|
146
|
+
/// @param account The account to check the session nonce for
|
|
147
|
+
/// @return The current active session nonce
|
|
90
148
|
function getActiveVouchersSessionNonce(address account) public view returns (bytes32) {
|
|
91
149
|
return getAacStorage().activeVouchersSessionNonce[account];
|
|
92
150
|
}
|
|
93
151
|
|
|
94
|
-
/// @notice
|
|
152
|
+
/// @notice Invalidates all previously signed vouchers by updating the session nonce
|
|
153
|
+
/// @dev Generates a new random nonce using the caller's address and block.prevrandao.
|
|
154
|
+
/// Any vouchers signed with the previous nonce become invalid immediately.
|
|
155
|
+
/// Call this if you suspect voucher compromise or want to revoke all delegations.
|
|
95
156
|
function updateActiveVouchersSessionNonce() external {
|
|
96
157
|
getAacStorage().activeVouchersSessionNonce[msg.sender] =
|
|
97
158
|
keccak256(abi.encodePacked(msg.sender, block.prevrandao));
|
|
@@ -7,15 +7,22 @@ import {EventCounter} from "../primitives/EventCounter.sol";
|
|
|
7
7
|
import {VerifierAddressGetter} from "../primitives/VerifierAddressGetter.sol";
|
|
8
8
|
import {AllowanceProof} from "../AccessControl/AdvancedAccessControl.types.sol";
|
|
9
9
|
|
|
10
|
+
/// @title AccessControlListStorage
|
|
11
|
+
/// @notice Storage layout for the Access Control List (ACL).
|
|
12
|
+
/// @dev Uses ERC-7201 namespaced storage pattern to avoid storage collisions.
|
|
10
13
|
contract AccessControlListStorage {
|
|
11
14
|
|
|
15
|
+
/// @dev Storage struct for ACL state.
|
|
12
16
|
struct AclStorage {
|
|
17
|
+
/// @dev Maps (handle, account) pairs to persistent access permissions.
|
|
13
18
|
mapping(bytes32 handle => mapping(address account => bool isAllowed)) persistedAllowedPairs;
|
|
19
|
+
/// @dev Maps handles to whether they are revealed (publicly accessible).
|
|
14
20
|
mapping(bytes32 handle => bool isAllowed) persistedAllowedForDecryption;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
bytes32 private constant ACL_STORAGE_LOCATION = keccak256("inco.storage.ACL");
|
|
18
24
|
|
|
25
|
+
/// @dev Returns a pointer to the ACL storage struct.
|
|
19
26
|
function getAclStorage() internal pure returns (AclStorage storage $) {
|
|
20
27
|
bytes32 loc = ACL_STORAGE_LOCATION;
|
|
21
28
|
assembly {
|
|
@@ -25,6 +32,16 @@ contract AccessControlListStorage {
|
|
|
25
32
|
|
|
26
33
|
}
|
|
27
34
|
|
|
35
|
+
/// @title BaseAccessControlList
|
|
36
|
+
/// @notice Manages access permissions for encrypted handles.
|
|
37
|
+
/// @dev Implements a two-tier permission system:
|
|
38
|
+
/// - **Persistent permissions**: Stored permanently, survive across transactions.
|
|
39
|
+
/// - **Transient permissions**: Stored in transient storage (EIP-1153), cleared at end of transaction.
|
|
40
|
+
///
|
|
41
|
+
/// Access is granted if any of these conditions are true:
|
|
42
|
+
/// 1. Transient permission exists for (handle, account)
|
|
43
|
+
/// 2. Persistent permission exists for (handle, account)
|
|
44
|
+
/// 3. The handle has been revealed (public access)
|
|
28
45
|
abstract contract BaseAccessControlList is
|
|
29
46
|
IBaseAccessControlList,
|
|
30
47
|
AccessControlListStorage,
|
|
@@ -32,19 +49,36 @@ abstract contract BaseAccessControlList is
|
|
|
32
49
|
EventCounter
|
|
33
50
|
{
|
|
34
51
|
|
|
52
|
+
/// @notice Thrown when proof verification fails during handle claiming.
|
|
53
|
+
/// @param verifyingContract The contract that was supposed to verify.
|
|
54
|
+
/// @param callFunction The function selector that was called.
|
|
55
|
+
/// @param argData The argument data that failed verification.
|
|
35
56
|
error ProofVerificationFailed(address verifyingContract, bytes4 callFunction, bytes argData);
|
|
36
57
|
|
|
58
|
+
/// @notice Emitted when persistent access is granted to an account for a handle.
|
|
59
|
+
/// @param handle The encrypted handle.
|
|
60
|
+
/// @param account The account granted access.
|
|
61
|
+
/// @param eventId The unique event ID.
|
|
37
62
|
event Allow(bytes32 handle, address account, uint256 eventId);
|
|
38
63
|
|
|
64
|
+
/// @notice Emitted when a handle is revealed for public access.
|
|
65
|
+
/// @param handle The encrypted handle that was revealed.
|
|
66
|
+
/// @param eventId The unique event ID.
|
|
39
67
|
event Reveal(bytes32 handle, uint256 eventId);
|
|
40
68
|
|
|
41
|
-
/// @
|
|
69
|
+
/// @notice Grants persistent access to an account for an encrypted handle.
|
|
70
|
+
/// @dev Caller must already have access to the handle. This permission survives across transactions.
|
|
71
|
+
/// @param handle The encrypted handle to grant access to.
|
|
72
|
+
/// @param account The account to grant access to.
|
|
42
73
|
function allow(bytes32 handle, address account) public {
|
|
43
74
|
require(isAllowed(handle, msg.sender), SenderNotAllowedForHandle(handle, msg.sender));
|
|
44
75
|
allowInternal(handle, account);
|
|
45
76
|
}
|
|
46
77
|
|
|
47
|
-
/// @
|
|
78
|
+
/// @notice Reveals a handle, granting permanent public access to anyone.
|
|
79
|
+
/// @dev Once revealed, the encrypted value can be decrypted by anyone. This is irreversible.
|
|
80
|
+
/// Caller must have access to the handle.
|
|
81
|
+
/// @param handle The encrypted handle to reveal.
|
|
48
82
|
function reveal(bytes32 handle) public {
|
|
49
83
|
require(isAllowed(handle, msg.sender), SenderNotAllowedForHandle(handle, msg.sender));
|
|
50
84
|
AclStorage storage $ = getAclStorage();
|
|
@@ -54,7 +88,9 @@ abstract contract BaseAccessControlList is
|
|
|
54
88
|
setDigest(abi.encodePacked(EOps.Reveal, handle, id));
|
|
55
89
|
}
|
|
56
90
|
|
|
57
|
-
/// @dev persistent
|
|
91
|
+
/// @dev Internal function to grant persistent access without ownership check.
|
|
92
|
+
/// @param handle The encrypted handle to grant access to.
|
|
93
|
+
/// @param account The account to grant access to.
|
|
58
94
|
function allowInternal(bytes32 handle, address account) internal {
|
|
59
95
|
AclStorage storage $ = getAclStorage();
|
|
60
96
|
$.persistedAllowedPairs[handle][account] = true;
|
|
@@ -63,13 +99,20 @@ abstract contract BaseAccessControlList is
|
|
|
63
99
|
setDigest(abi.encodePacked(EOps.Allow, handle, account, id));
|
|
64
100
|
}
|
|
65
101
|
|
|
66
|
-
|
|
102
|
+
/// @notice Grants transient access to an account for an encrypted handle.
|
|
103
|
+
/// @dev Transient permissions are cleared at the end of the transaction (EIP-1153).
|
|
104
|
+
/// Caller must have access to the handle.
|
|
105
|
+
/// @param handle The encrypted handle to grant access to.
|
|
106
|
+
/// @param account The account to grant access to.
|
|
67
107
|
function allowTransient(bytes32 handle, address account) public {
|
|
68
108
|
require(isAllowed(handle, msg.sender), SenderNotAllowedForHandle(handle, msg.sender));
|
|
69
109
|
allowTransientInternal(handle, account);
|
|
70
110
|
}
|
|
71
111
|
|
|
72
|
-
/// @dev
|
|
112
|
+
/// @dev Internal function to grant transient access without ownership check.
|
|
113
|
+
/// Uses assembly to store in transient storage and track keys for cleanup.
|
|
114
|
+
/// @param handle The encrypted handle to grant access to.
|
|
115
|
+
/// @param account The account to grant access to.
|
|
73
116
|
function allowTransientInternal(bytes32 handle, address account) internal {
|
|
74
117
|
bytes32 key = keccak256(abi.encodePacked(handle, account));
|
|
75
118
|
assembly {
|
|
@@ -81,6 +124,10 @@ abstract contract BaseAccessControlList is
|
|
|
81
124
|
}
|
|
82
125
|
}
|
|
83
126
|
|
|
127
|
+
/// @notice Checks if transient access exists for a handle-account pair.
|
|
128
|
+
/// @param handle The encrypted handle to check.
|
|
129
|
+
/// @param account The account to check access for.
|
|
130
|
+
/// @return True if transient access is granted.
|
|
84
131
|
function allowedTransient(bytes32 handle, address account) public view returns (bool) {
|
|
85
132
|
bool isAllowedTransient;
|
|
86
133
|
bytes32 key = keccak256(abi.encodePacked(handle, account));
|
|
@@ -90,6 +137,9 @@ abstract contract BaseAccessControlList is
|
|
|
90
137
|
return isAllowedTransient;
|
|
91
138
|
}
|
|
92
139
|
|
|
140
|
+
/// @notice Clears all transient storage entries.
|
|
141
|
+
/// @dev Should be called at the end of a transaction to reset transient permissions.
|
|
142
|
+
/// Iterates through all stored keys and clears them.
|
|
93
143
|
function cleanTransientStorage() external {
|
|
94
144
|
assembly {
|
|
95
145
|
let length := tload(0)
|
|
@@ -103,6 +153,10 @@ abstract contract BaseAccessControlList is
|
|
|
103
153
|
}
|
|
104
154
|
}
|
|
105
155
|
|
|
156
|
+
/// @notice Claims access to a handle using a signed allowance proof.
|
|
157
|
+
/// @dev Verifies the proof via IncoVerifier and grants persistent access if valid.
|
|
158
|
+
/// @param handle The encrypted handle to claim access to.
|
|
159
|
+
/// @param proof The signed allowance proof from an authorized sharer.
|
|
106
160
|
function claimHandle(bytes32 handle, AllowanceProof memory proof) public {
|
|
107
161
|
require(
|
|
108
162
|
incoVerifier.isAllowedWithProof(handle, msg.sender, proof),
|
|
@@ -113,15 +167,27 @@ abstract contract BaseAccessControlList is
|
|
|
113
167
|
allowInternal(handle, msg.sender);
|
|
114
168
|
}
|
|
115
169
|
|
|
170
|
+
/// @notice Checks if persistent access exists for a handle-account pair.
|
|
171
|
+
/// @param handle The encrypted handle to check.
|
|
172
|
+
/// @param account The account to check access for.
|
|
173
|
+
/// @return True if persistent access is granted.
|
|
116
174
|
function persistAllowed(bytes32 handle, address account) public view returns (bool) {
|
|
117
175
|
AclStorage storage $ = getAclStorage();
|
|
118
176
|
return $.persistedAllowedPairs[handle][account];
|
|
119
177
|
}
|
|
120
178
|
|
|
179
|
+
/// @notice Checks if an account has access to an encrypted handle.
|
|
180
|
+
/// @dev Returns true if any of: transient access, persistent access, or handle is revealed.
|
|
181
|
+
/// @param handle The encrypted handle to check.
|
|
182
|
+
/// @param account The account to check access for.
|
|
183
|
+
/// @return True if the account has access to the handle.
|
|
121
184
|
function isAllowed(bytes32 handle, address account) public view returns (bool) {
|
|
122
185
|
return allowedTransient(handle, account) || persistAllowed(handle, account) || isRevealed(handle);
|
|
123
186
|
}
|
|
124
187
|
|
|
188
|
+
/// @notice Checks if a handle has been revealed for public access.
|
|
189
|
+
/// @param handle The encrypted handle to check.
|
|
190
|
+
/// @return True if the handle has been revealed.
|
|
125
191
|
function isRevealed(bytes32 handle) public view returns (bool) {
|
|
126
192
|
AclStorage storage $ = getAclStorage();
|
|
127
193
|
return $.persistedAllowedForDecryption[handle];
|
|
@@ -6,20 +6,33 @@ import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/crypt
|
|
|
6
6
|
import {SignatureVerifier} from "./primitives/SignatureVerifier.sol";
|
|
7
7
|
import {IDecryptionAttester} from "./interfaces/IDecryptionAttester.sol";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
/// @title DecryptionAttester
|
|
10
|
+
/// @notice Verifies decryption attestations signed by covalidators.
|
|
11
|
+
/// @dev When a user requests decryption of an encrypted handle, the covalidator decrypts
|
|
12
|
+
/// the value and signs an attestation. This contract verifies that attestation using EIP-712
|
|
13
|
+
/// typed data signatures. The attestation binds a handle to its decrypted value, allowing
|
|
14
|
+
/// on-chain verification that a decryption was performed correctly by authorized covalidators.
|
|
12
15
|
abstract contract DecryptionAttester is IDecryptionAttester, SignatureVerifier, EIP712Upgradeable {
|
|
13
16
|
|
|
17
|
+
/// @dev EIP-712 type hash for DecryptionAttestation struct.
|
|
14
18
|
bytes32 constant DECRYPTION_ATTESTATION_STRUCT_HASH =
|
|
15
19
|
keccak256("DecryptionAttestation(bytes32 handle,bytes32 value)");
|
|
16
20
|
|
|
21
|
+
/// @notice Computes the EIP-712 digest for a decryption attestation.
|
|
22
|
+
/// @dev Used to verify signatures from covalidators attesting to a decryption result.
|
|
23
|
+
/// @param decryption The decryption attestation containing the handle and decrypted value.
|
|
24
|
+
/// @return The EIP-712 typed data hash that should be signed by covalidators.
|
|
17
25
|
function decryptionAttestationDigest(DecryptionAttestation memory decryption) public view returns (bytes32) {
|
|
18
26
|
return _hashTypedDataV4(
|
|
19
27
|
keccak256(abi.encode(DECRYPTION_ATTESTATION_STRUCT_HASH, decryption.handle, decryption.value))
|
|
20
28
|
);
|
|
21
29
|
}
|
|
22
30
|
|
|
31
|
+
/// @notice Validates a decryption attestation against covalidator signatures.
|
|
32
|
+
/// @dev Verifies that the attestation was signed by the required threshold of covalidators.
|
|
33
|
+
/// @param decryption The decryption attestation to validate.
|
|
34
|
+
/// @param signatures Array of signatures from covalidators.
|
|
35
|
+
/// @return True if the attestation has valid signatures from the required covalidators.
|
|
23
36
|
function isValidDecryptionAttestation(DecryptionAttestation memory decryption, bytes[] memory signatures)
|
|
24
37
|
public
|
|
25
38
|
view
|
|
@@ -8,8 +8,14 @@ import {IEncryptedInput} from "./interfaces/IEncryptedInput.sol";
|
|
|
8
8
|
import {HandleAlreadyExists} from "../Errors.sol";
|
|
9
9
|
import {Fee} from "./Fee.sol";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/// @notice Thrown when the handle computed on-chain doesn't match the handle prepended to the input.
|
|
12
|
+
/// @dev This typically indicates a mismatch between client-side encryption context and on-chain context.
|
|
13
|
+
/// @param externalHandle The handle prepended to the input by the client.
|
|
14
|
+
/// @param computedHandle The handle computed on-chain from the ciphertext and context.
|
|
15
|
+
/// @param chainId The chain ID used in handle computation.
|
|
16
|
+
/// @param aclAddress The ACL contract address used in handle computation.
|
|
17
|
+
/// @param userAddress The user address used in handle computation.
|
|
18
|
+
/// @param contractAddress The contract address used in handle computation.
|
|
13
19
|
error ExternalHandleDoesNotMatchComputedHandle(
|
|
14
20
|
bytes32 externalHandle,
|
|
15
21
|
bytes32 computedHandle,
|
|
@@ -17,52 +23,101 @@ error ExternalHandleDoesNotMatchComputedHandle(
|
|
|
17
23
|
uint256 chainId,
|
|
18
24
|
address aclAddress,
|
|
19
25
|
address userAddress,
|
|
20
|
-
address contractAddress
|
|
26
|
+
address contractAddress,
|
|
27
|
+
int32 version
|
|
21
28
|
);
|
|
22
29
|
|
|
30
|
+
/// @title EncryptedInput
|
|
31
|
+
/// @notice Handles the submission of client-encrypted values to create on-chain encrypted handles.
|
|
32
|
+
/// @dev Users encrypt values client-side and submit them with a prepended handle as a checksum.
|
|
33
|
+
/// The contract verifies the handle matches the on-chain computation to ensure context consistency.
|
|
34
|
+
/// This is a paid operation - users must send ETH to cover processing costs.
|
|
23
35
|
abstract contract EncryptedInput is IEncryptedInput, BaseAccessControlList, HandleGeneration, Fee {
|
|
24
36
|
|
|
37
|
+
/// @notice Emitted when a new encrypted input is submitted.
|
|
38
|
+
/// @param result The generated handle for the encrypted value.
|
|
39
|
+
/// @param contractAddress The contract that submitted the input.
|
|
40
|
+
/// @param user The user who encrypted the value.
|
|
41
|
+
/// @param ciphertext The encrypted data (without the prepended handle).
|
|
42
|
+
/// @param eventId The unique event ID for ordering.
|
|
25
43
|
event NewInput(
|
|
26
|
-
bytes32 indexed result,
|
|
44
|
+
bytes32 indexed result,
|
|
45
|
+
address indexed contractAddress,
|
|
46
|
+
address indexed user,
|
|
47
|
+
int32 version,
|
|
48
|
+
bytes ciphertext,
|
|
49
|
+
uint256 eventId
|
|
27
50
|
);
|
|
28
51
|
|
|
29
|
-
|
|
52
|
+
/// @notice Submits a client-encrypted uint256 value to create an encrypted handle.
|
|
53
|
+
/// @dev This is a paid operation. The input must contain a prepended handle for verification.
|
|
54
|
+
/// @param input The encrypted input with prepended handle checksum.
|
|
55
|
+
/// @param user The user address that encrypted the value.
|
|
56
|
+
/// @return newValue The encrypted uint256 handle.
|
|
57
|
+
function newEuint256(bytes calldata input, address user) external payable returns (euint256 newValue) {
|
|
30
58
|
return euint256.wrap(newInput(input, user, ETypes.Uint256));
|
|
31
59
|
}
|
|
32
60
|
|
|
33
|
-
|
|
61
|
+
/// @notice Submits a client-encrypted boolean value to create an encrypted handle.
|
|
62
|
+
/// @dev This is a paid operation. The input must contain a prepended handle for verification.
|
|
63
|
+
/// @param input The encrypted input with prepended handle checksum.
|
|
64
|
+
/// @param user The user address that encrypted the value.
|
|
65
|
+
/// @return newValue The encrypted boolean handle.
|
|
66
|
+
function newEbool(bytes calldata input, address user) external payable returns (ebool newValue) {
|
|
34
67
|
return ebool.wrap(newInput(input, user, ETypes.Bool));
|
|
35
68
|
}
|
|
36
69
|
|
|
37
|
-
|
|
70
|
+
/// @notice Submits a client-encrypted address value to create an encrypted handle.
|
|
71
|
+
/// @dev This is a paid operation. The input must contain a prepended handle for verification.
|
|
72
|
+
/// @param input The encrypted input with prepended handle checksum.
|
|
73
|
+
/// @param user The user address that encrypted the value.
|
|
74
|
+
/// @return newValue The encrypted address handle.
|
|
75
|
+
function newEaddress(bytes calldata input, address user) external payable returns (eaddress newValue) {
|
|
38
76
|
return eaddress.wrap(newInput(input, user, ETypes.AddressOrUint160OrBytes20));
|
|
39
77
|
}
|
|
40
78
|
|
|
41
|
-
function
|
|
79
|
+
/// @dev Internal function to process encrypted input with fee payment.
|
|
80
|
+
/// @param user The user address that encrypted the value.
|
|
81
|
+
/// @param inputType The type of encrypted value.
|
|
82
|
+
/// @return newHandle The generated handle.
|
|
83
|
+
function newInput(bytes calldata input, address user, ETypes inputType)
|
|
42
84
|
internal
|
|
43
85
|
paying
|
|
44
86
|
returns (bytes32 newHandle)
|
|
45
87
|
{
|
|
46
|
-
newHandle = _newInput(
|
|
88
|
+
newHandle = _newInput(input, user, inputType);
|
|
47
89
|
}
|
|
48
90
|
|
|
49
|
-
function
|
|
91
|
+
/// @dev Internal function to process encrypted input without fee payment.
|
|
92
|
+
/// Used for internal operations that don't require user payment.
|
|
93
|
+
/// @param user The user address that encrypted the value.
|
|
94
|
+
/// @param inputType The type of encrypted value.
|
|
95
|
+
/// @return newHandle The generated handle.
|
|
96
|
+
function newInputNotPaying(bytes calldata input, address user, ETypes inputType)
|
|
50
97
|
internal
|
|
51
98
|
returns (bytes32 newHandle)
|
|
52
99
|
{
|
|
53
|
-
newHandle = _newInput(
|
|
100
|
+
newHandle = _newInput(input, user, inputType);
|
|
54
101
|
}
|
|
55
102
|
|
|
56
103
|
/// @notice Creates a new input with a prepended handle as a checksum.
|
|
104
|
+
/// @dev Verifies the external handle matches the computed handle, ensures the handle
|
|
105
|
+
/// doesn't already exist, emits the NewInput event, and grants access to the user
|
|
106
|
+
/// and calling contract.
|
|
57
107
|
/// @param input The input that contains the handle prepended to the ciphertext.
|
|
58
108
|
/// @param user The user address associated with the input.
|
|
59
|
-
|
|
109
|
+
/// @param inputType The type of encrypted value being created.
|
|
110
|
+
/// @return handle The generated handle for the encrypted value.
|
|
111
|
+
function _newInput(bytes calldata input, address user, ETypes inputType) private returns (bytes32 handle) {
|
|
60
112
|
// Since there is no sensible way to handle abi.decode errors (https://github.com/argotorg/solidity/issues/10381)
|
|
61
113
|
// at least fail early on a conservative minimum length
|
|
62
|
-
require(input.length >=
|
|
114
|
+
require(input.length >= 68, "Input too short, should be at least 68 bytes");
|
|
115
|
+
// Parse the version from the first 4 bytes.
|
|
116
|
+
bytes4 prefix = bytes4(input[:4]);
|
|
117
|
+
int32 version = int32(uint32(prefix));
|
|
63
118
|
// Remove external handle prepended to input as a checksum
|
|
64
|
-
(bytes32 externalHandle, bytes memory ciphertext) = abi.decode(input, (bytes32, bytes));
|
|
65
|
-
handle = getInputHandle(ciphertext, user, msg.sender, inputType);
|
|
119
|
+
(bytes32 externalHandle, bytes memory ciphertext) = abi.decode(input[4:], (bytes32, bytes));
|
|
120
|
+
handle = getInputHandle(ciphertext, user, msg.sender, version, inputType);
|
|
66
121
|
require(
|
|
67
122
|
handle == externalHandle,
|
|
68
123
|
ExternalHandleDoesNotMatchComputedHandle({
|
|
@@ -71,14 +126,22 @@ abstract contract EncryptedInput is IEncryptedInput, BaseAccessControlList, Hand
|
|
|
71
126
|
chainId: block.chainid,
|
|
72
127
|
aclAddress: address(this),
|
|
73
128
|
userAddress: user,
|
|
74
|
-
contractAddress: msg.sender
|
|
129
|
+
contractAddress: msg.sender,
|
|
130
|
+
version: version
|
|
75
131
|
})
|
|
76
132
|
);
|
|
77
133
|
// We assume that providing the same handle (which via HADU implies same plaintext, same context, and same
|
|
78
134
|
// instance of encryption)
|
|
79
135
|
require(!isAllowed(handle, user), HandleAlreadyExists(handle));
|
|
80
136
|
uint256 id = getNextEventId();
|
|
81
|
-
emit NewInput({
|
|
137
|
+
emit NewInput({
|
|
138
|
+
result: handle,
|
|
139
|
+
contractAddress: msg.sender,
|
|
140
|
+
user: user,
|
|
141
|
+
version: version,
|
|
142
|
+
ciphertext: ciphertext,
|
|
143
|
+
eventId: id
|
|
144
|
+
});
|
|
82
145
|
setDigest(abi.encodePacked(handle, id));
|
|
83
146
|
// We allow to user since this is harmless and it is convenient to use the allow mapping to track inputs.
|
|
84
147
|
// NOTE: the allow must come after emitting the new input event, since allow emits its own event.
|