@inco/lightning 0.10.0-devnet-2 → 0.11.0-demonet-1
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/manifest.yaml +46 -0
- package/package.json +1 -1
- package/src/IncoLightning.sol +1 -3
- package/src/IncoVerifier.sol +1 -3
- package/src/Lib.alphanet.sol +19 -0
- package/src/Lib.demonet.sol +20 -1
- package/src/Lib.devnet.sol +19 -0
- package/src/Lib.sol +20 -1
- package/src/Lib.template.sol +19 -0
- package/src/Lib.testnet.sol +20 -1
- package/src/Types.sol +0 -1
- package/src/interfaces/automata-interfaces/IPCCSRouterExtended.sol +2 -0
- package/src/libs/incoLightning_alphanet_v0_297966649.sol +19 -0
- package/src/libs/incoLightning_alphanet_v1_725458969.sol +19 -0
- package/src/libs/incoLightning_alphanet_v2_976644394.sol +19 -0
- package/src/libs/incoLightning_demonet_v0_863421733.sol +19 -0
- package/src/libs/incoLightning_demonet_v11_473256067.sol +1242 -0
- package/src/libs/incoLightning_demonet_v2_467437523.sol +19 -0
- package/src/libs/incoLightning_devnet_v0_340846814.sol +19 -0
- package/src/libs/incoLightning_devnet_v10_266391127.sol +19 -0
- package/src/libs/incoLightning_devnet_v1_904635675.sol +19 -0
- package/src/libs/incoLightning_devnet_v2_295237520.sol +19 -0
- package/src/libs/incoLightning_devnet_v3_976859633.sol +19 -0
- package/src/libs/incoLightning_devnet_v4_409204766.sol +19 -0
- package/src/libs/incoLightning_devnet_v5_203964628.sol +19 -0
- package/src/libs/incoLightning_devnet_v6_281949651.sol +19 -0
- package/src/libs/incoLightning_devnet_v7_24560427.sol +19 -0
- package/src/libs/incoLightning_devnet_v8_985328058.sol +19 -0
- package/src/libs/incoLightning_devnet_v9_269218568.sol +19 -0
- package/src/libs/incoLightning_testnet_v0_183408998.sol +19 -0
- package/src/libs/incoLightning_testnet_v11_340321378.sol +1242 -0
- package/src/libs/incoLightning_testnet_v2_889158349.sol +19 -0
- package/src/lightning-parts/AccessControl/AdvancedAccessControl.sol +13 -2
- package/src/lightning-parts/AccessControl/AdvancedAccessControl.types.sol +12 -0
- package/src/lightning-parts/AccessControl/test/TestAdvancedAccessControl.t.sol +36 -9
- package/src/lightning-parts/EncryptedOperations.sol +2 -9
- package/src/lightning-parts/primitives/EListHandleMetadata.sol +6 -6
- package/src/lightning-parts/primitives/HandleGeneration.sol +3 -10
- package/src/lightning-parts/primitives/HandleMetadata.sol +10 -10
- package/src/lightning-parts/test/HandleMetadata.t.sol +22 -40
- package/src/periphery/SessionVerifier.sol +16 -14
- package/src/test/IncoTest.sol +19 -3
- package/src/test/TestLib.t.sol +65 -13
- package/src/test/TestUpgrade.t.sol +1 -1
- package/src/version/IncoLightningConfig.sol +2 -2
|
@@ -801,6 +801,13 @@ library e {
|
|
|
801
801
|
inco.allow(eaddress.unwrap(a), to);
|
|
802
802
|
}
|
|
803
803
|
|
|
804
|
+
/// @notice Allows an address to access an elist
|
|
805
|
+
/// @param a The elist
|
|
806
|
+
/// @param to The address to allow
|
|
807
|
+
function allow(elist a, address to) internal {
|
|
808
|
+
inco.allow(elist.unwrap(a), to);
|
|
809
|
+
}
|
|
810
|
+
|
|
804
811
|
/// @notice Reveals an encrypted uint256
|
|
805
812
|
/// @param a The encrypted uint256
|
|
806
813
|
function reveal(euint256 a) internal {
|
|
@@ -819,6 +826,12 @@ library e {
|
|
|
819
826
|
inco.reveal(eaddress.unwrap(a));
|
|
820
827
|
}
|
|
821
828
|
|
|
829
|
+
/// @notice Reveals an elist (makes all elements publicly readable without wallet authentication)
|
|
830
|
+
/// @param a The elist
|
|
831
|
+
function reveal(elist a) internal {
|
|
832
|
+
inco.reveal(elist.unwrap(a));
|
|
833
|
+
}
|
|
834
|
+
|
|
822
835
|
/// @notice Verifies a decryption attestation for a euint256
|
|
823
836
|
/// @param handle The encrypted handle
|
|
824
837
|
/// @param value The claimed decrypted value
|
|
@@ -857,6 +870,12 @@ library e {
|
|
|
857
870
|
allow(a, address(this));
|
|
858
871
|
}
|
|
859
872
|
|
|
873
|
+
/// @notice Allows this contract to access an elist
|
|
874
|
+
/// @param a The elist
|
|
875
|
+
function allowThis(elist a) internal {
|
|
876
|
+
allow(a, address(this));
|
|
877
|
+
}
|
|
878
|
+
|
|
860
879
|
/// @notice Checks if a user is allowed to access an encrypted uint256
|
|
861
880
|
/// @param user The address to check
|
|
862
881
|
/// @param a The encrypted uint256
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
// SPDX-License-Identifier: No License
|
|
2
2
|
pragma solidity ^0.8;
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
AllowanceVoucher,
|
|
6
|
+
AllowanceProof,
|
|
7
|
+
REQUIRED_ALLOWANCE_VOUCHER_WARNING_HASH
|
|
8
|
+
} from "./AdvancedAccessControl.types.sol";
|
|
5
9
|
import {ALLOWANCE_GRANTED_MAGIC_VALUE} from "../../Types.sol";
|
|
6
10
|
import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
|
|
7
11
|
import {IAdvancedAccessControl, IVoucherEip712Checker} from "./interfaces/IAdvancedAccessControl.sol";
|
|
@@ -47,7 +51,7 @@ abstract contract VoucherEip712Checker is IVoucherEip712Checker, EIP712Upgradeab
|
|
|
47
51
|
/// @notice EIP-712 type hash for the AllowanceVoucher struct
|
|
48
52
|
/// @dev Computed once at compile time for gas efficiency
|
|
49
53
|
bytes32 constant ALLOWANCE_VOUCHER_STRUCT_HASH = keccak256(
|
|
50
|
-
"AllowanceVoucher(bytes32 sessionNonce,address verifyingContract,bytes4 callFunction,bytes sharerArgData)"
|
|
54
|
+
"AllowanceVoucher(string warning,bytes32 sessionNonce,address verifyingContract,bytes4 callFunction,bytes sharerArgData)"
|
|
51
55
|
);
|
|
52
56
|
|
|
53
57
|
/// @notice Computes the EIP-712 digest for an allowance voucher
|
|
@@ -60,6 +64,7 @@ abstract contract VoucherEip712Checker is IVoucherEip712Checker, EIP712Upgradeab
|
|
|
60
64
|
keccak256(
|
|
61
65
|
abi.encode(
|
|
62
66
|
ALLOWANCE_VOUCHER_STRUCT_HASH,
|
|
67
|
+
keccak256(bytes(voucher.warning)),
|
|
63
68
|
voucher.sessionNonce,
|
|
64
69
|
voucher.verifyingContract,
|
|
65
70
|
voucher.callFunction,
|
|
@@ -95,6 +100,9 @@ abstract contract AdvancedAccessControl is
|
|
|
95
100
|
|
|
96
101
|
using SignatureChecker for address;
|
|
97
102
|
|
|
103
|
+
/// @notice Thrown when a voucher's warning field does not match the required warning text
|
|
104
|
+
error InvalidVoucherWarning();
|
|
105
|
+
|
|
98
106
|
/// @notice Thrown when a voucher signature is invalid for the claimed signer
|
|
99
107
|
/// @param signer The address that allegedly signed the voucher
|
|
100
108
|
/// @param digest The EIP-712 digest that should have been signed
|
|
@@ -121,6 +129,9 @@ abstract contract AdvancedAccessControl is
|
|
|
121
129
|
/// @param proof The allowance proof containing voucher, signature, and requester data
|
|
122
130
|
/// @return True if access is allowed, false or reverts otherwise
|
|
123
131
|
function isAllowedWithProof(bytes32 handle, address account, AllowanceProof memory proof) public returns (bool) {
|
|
132
|
+
require(
|
|
133
|
+
keccak256(bytes(proof.voucher.warning)) == REQUIRED_ALLOWANCE_VOUCHER_WARNING_HASH, InvalidVoucherWarning()
|
|
134
|
+
);
|
|
124
135
|
require(proof.voucher.verifyingContract != address(0), InvalidVerifyingContract());
|
|
125
136
|
require(
|
|
126
137
|
IBaseAccessControlList(incoLightningAddress).isAllowed(handle, proof.sharer),
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
// SPDX-License-Identifier: No License
|
|
2
2
|
pragma solidity ^0.8;
|
|
3
3
|
|
|
4
|
+
// Required warning that must be present in every AllowanceVoucher.
|
|
5
|
+
// Must match SESSION_KEY_WARNING in js/src/advancedacl/session-key.ts.
|
|
6
|
+
string constant REQUIRED_ALLOWANCE_VOUCHER_WARNING =
|
|
7
|
+
"Inco Warning: signing this message may leak your private data, including from unrelated apps. Sign only if you fully trust this app.";
|
|
8
|
+
|
|
9
|
+
// Precomputed hash of REQUIRED_ALLOWANCE_VOUCHER_WARNING for cheap runtime comparison.
|
|
10
|
+
// Evaluated at compile time — no storage slot used, no runtime keccak256 of the full string.
|
|
11
|
+
bytes32 constant REQUIRED_ALLOWANCE_VOUCHER_WARNING_HASH = keccak256(bytes(REQUIRED_ALLOWANCE_VOUCHER_WARNING));
|
|
12
|
+
|
|
4
13
|
// can be for arbitrary handles to arbitrary accounts
|
|
5
14
|
// signed by the account sharing its read access
|
|
6
15
|
struct AllowanceVoucher {
|
|
16
|
+
// Human-readable warning displayed by wallets at signing time.
|
|
17
|
+
// Must be first so wallets show it before the opaque byte fields below.
|
|
18
|
+
string warning;
|
|
7
19
|
bytes32 sessionNonce;
|
|
8
20
|
address verifyingContract;
|
|
9
21
|
bytes4 callFunction;
|
|
@@ -4,6 +4,7 @@ pragma solidity ^0.8;
|
|
|
4
4
|
import {IncoTest} from "../../../test/IncoTest.sol";
|
|
5
5
|
import {SessionVerifier, Session} from "../../../periphery/SessionVerifier.sol";
|
|
6
6
|
import {AllowanceVoucher, AllowanceProof} from "../AdvancedAccessControl.sol";
|
|
7
|
+
import {REQUIRED_ALLOWANCE_VOUCHER_WARNING} from "../AdvancedAccessControl.types.sol";
|
|
7
8
|
import {euint256, SharerNotAllowedForHandle} from "../../../Types.sol";
|
|
8
9
|
import {e, inco} from "../../../Lib.sol";
|
|
9
10
|
import {AdvancedAccessControl} from "../AdvancedAccessControl.sol";
|
|
@@ -93,7 +94,8 @@ contract TestAdvancedAccessControl is IncoTest {
|
|
|
93
94
|
sessionNonce: bytes32(0),
|
|
94
95
|
verifyingContract: address(0),
|
|
95
96
|
callFunction: SessionVerifier.canUseSession.selector,
|
|
96
|
-
sharerArgData: abi.encode(Session({decrypter: bob, expiresAt: block.timestamp + 1 days}))
|
|
97
|
+
sharerArgData: abi.encode(Session({decrypter: bob, expiresAt: block.timestamp + 1 days})),
|
|
98
|
+
warning: REQUIRED_ALLOWANCE_VOUCHER_WARNING
|
|
97
99
|
});
|
|
98
100
|
AllowanceProof memory bobsProof = getBobsProof(invalidSessionVoucher);
|
|
99
101
|
vm.expectRevert(abi.encodeWithSelector(AdvancedAccessControl.InvalidVerifyingContract.selector));
|
|
@@ -111,7 +113,8 @@ contract TestAdvancedAccessControl is IncoTest {
|
|
|
111
113
|
sessionNonce: bytes32(0),
|
|
112
114
|
verifyingContract: address(sessionVerifier),
|
|
113
115
|
callFunction: SessionVerifier.canUseSession.selector,
|
|
114
|
-
sharerArgData: abi.encode(Session({decrypter: bob, expiresAt: block.timestamp + 1 days}))
|
|
116
|
+
sharerArgData: abi.encode(Session({decrypter: bob, expiresAt: block.timestamp + 1 days})),
|
|
117
|
+
warning: REQUIRED_ALLOWANCE_VOUCHER_WARNING
|
|
115
118
|
});
|
|
116
119
|
AllowanceProof memory bobsProof = getBobsProof(aliceSessionVoucherForBob);
|
|
117
120
|
assertTrue(
|
|
@@ -128,7 +131,8 @@ contract TestAdvancedAccessControl is IncoTest {
|
|
|
128
131
|
sessionNonce: bytes32(0),
|
|
129
132
|
verifyingContract: address(verifier),
|
|
130
133
|
callFunction: verifier.someCheck.selector,
|
|
131
|
-
sharerArgData: ""
|
|
134
|
+
sharerArgData: "",
|
|
135
|
+
warning: REQUIRED_ALLOWANCE_VOUCHER_WARNING
|
|
132
136
|
});
|
|
133
137
|
AllowanceProof memory bobsFirstProof = getBobsProof(voucher);
|
|
134
138
|
assertTrue(
|
|
@@ -140,7 +144,8 @@ contract TestAdvancedAccessControl is IncoTest {
|
|
|
140
144
|
sessionNonce: madeUpNonce,
|
|
141
145
|
verifyingContract: address(verifier),
|
|
142
146
|
callFunction: verifier.someCheck.selector,
|
|
143
|
-
sharerArgData: ""
|
|
147
|
+
sharerArgData: "",
|
|
148
|
+
warning: REQUIRED_ALLOWANCE_VOUCHER_WARNING
|
|
144
149
|
});
|
|
145
150
|
AllowanceProof memory invalidBobProof = getBobsProof(voucher);
|
|
146
151
|
// the session nonce should be checked by inco
|
|
@@ -164,7 +169,8 @@ contract TestAdvancedAccessControl is IncoTest {
|
|
|
164
169
|
sessionNonce: alicesNewNonce,
|
|
165
170
|
verifyingContract: address(verifier),
|
|
166
171
|
callFunction: verifier.someCheck.selector,
|
|
167
|
-
sharerArgData: ""
|
|
172
|
+
sharerArgData: "",
|
|
173
|
+
warning: REQUIRED_ALLOWANCE_VOUCHER_WARNING
|
|
168
174
|
});
|
|
169
175
|
AllowanceProof memory bobsSecondProof = getBobsProof(voucher);
|
|
170
176
|
assertTrue(
|
|
@@ -179,7 +185,8 @@ contract TestAdvancedAccessControl is IncoTest {
|
|
|
179
185
|
sessionNonce: bytes32(0),
|
|
180
186
|
verifyingContract: address(verifier),
|
|
181
187
|
callFunction: verifier.someCheck.selector,
|
|
182
|
-
sharerArgData: abi.encode(SomeVerifier.SharerArg({handleShared: secretHandle, allowedAccount: bob}))
|
|
188
|
+
sharerArgData: abi.encode(SomeVerifier.SharerArg({handleShared: secretHandle, allowedAccount: bob})),
|
|
189
|
+
warning: REQUIRED_ALLOWANCE_VOUCHER_WARNING
|
|
183
190
|
});
|
|
184
191
|
AllowanceProof memory bobsProof = AllowanceProof({
|
|
185
192
|
sharer: alice,
|
|
@@ -217,7 +224,8 @@ contract TestAdvancedAccessControl is IncoTest {
|
|
|
217
224
|
sessionNonce: bytes32(0),
|
|
218
225
|
verifyingContract: address(verifier),
|
|
219
226
|
callFunction: verifier.someCheck.selector,
|
|
220
|
-
sharerArgData: ""
|
|
227
|
+
sharerArgData: "",
|
|
228
|
+
warning: REQUIRED_ALLOWANCE_VOUCHER_WARNING
|
|
221
229
|
});
|
|
222
230
|
// Use bob as sharer, but bob is NOT allowed on the secret (only alice is)
|
|
223
231
|
AllowanceProof memory proof = AllowanceProof({
|
|
@@ -237,7 +245,8 @@ contract TestAdvancedAccessControl is IncoTest {
|
|
|
237
245
|
sessionNonce: bytes32(0),
|
|
238
246
|
verifyingContract: address(verifier),
|
|
239
247
|
callFunction: verifier.someCheck.selector,
|
|
240
|
-
sharerArgData: ""
|
|
248
|
+
sharerArgData: "",
|
|
249
|
+
warning: REQUIRED_ALLOWANCE_VOUCHER_WARNING
|
|
241
250
|
});
|
|
242
251
|
// Alice is the sharer (and is allowed), but we sign with Bob's key
|
|
243
252
|
bytes memory wrongSignature = getSignatureForDigest(incoVerifier.allowanceVoucherDigest(voucher), bobPrivKey);
|
|
@@ -252,6 +261,23 @@ contract TestAdvancedAccessControl is IncoTest {
|
|
|
252
261
|
incoVerifier.isAllowedWithProof(secretHandle, bob, proof);
|
|
253
262
|
}
|
|
254
263
|
|
|
264
|
+
/// @notice Test InvalidVoucherWarning error when voucher warning does not match required text
|
|
265
|
+
function testIsAllowedWithProofInvalidVoucherWarning() public {
|
|
266
|
+
DoesNotVerifyAnything verifier = new DoesNotVerifyAnything();
|
|
267
|
+
AllowanceVoucher memory voucher = AllowanceVoucher({
|
|
268
|
+
sessionNonce: bytes32(0),
|
|
269
|
+
verifyingContract: address(verifier),
|
|
270
|
+
callFunction: verifier.someCheck.selector,
|
|
271
|
+
sharerArgData: "",
|
|
272
|
+
warning: "wrong warning"
|
|
273
|
+
});
|
|
274
|
+
AllowanceProof memory proof = AllowanceProof({
|
|
275
|
+
sharer: alice, voucher: voucher, voucherSignature: getAliceSig(voucher), requesterArgData: ""
|
|
276
|
+
});
|
|
277
|
+
vm.expectRevert(abi.encodeWithSelector(AdvancedAccessControl.InvalidVoucherWarning.selector));
|
|
278
|
+
incoVerifier.isAllowedWithProof(secretHandle, bob, proof);
|
|
279
|
+
}
|
|
280
|
+
|
|
255
281
|
/// @notice Test claimHandle fails when proof verification fails (line 107)
|
|
256
282
|
function testClaimHandleProofVerificationFailed() public {
|
|
257
283
|
SomeVerifier verifier = new SomeVerifier();
|
|
@@ -261,7 +287,8 @@ contract TestAdvancedAccessControl is IncoTest {
|
|
|
261
287
|
sessionNonce: bytes32(0),
|
|
262
288
|
verifyingContract: address(verifier),
|
|
263
289
|
callFunction: verifier.someCheck.selector,
|
|
264
|
-
sharerArgData: abi.encode(SomeVerifier.SharerArg({handleShared: secretHandle, allowedAccount: bob}))
|
|
290
|
+
sharerArgData: abi.encode(SomeVerifier.SharerArg({handleShared: secretHandle, allowedAccount: bob})),
|
|
291
|
+
warning: REQUIRED_ALLOWANCE_VOUCHER_WARNING
|
|
265
292
|
});
|
|
266
293
|
AllowanceProof memory proof = AllowanceProof({
|
|
267
294
|
sharer: alice, // alice IS allowed on the secret
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
EOps,
|
|
8
8
|
SenderNotAllowedForHandle,
|
|
9
9
|
ETypes,
|
|
10
|
+
UnexpectedType,
|
|
11
|
+
UnsupportedType,
|
|
10
12
|
isTypeSupported,
|
|
11
13
|
canCastTo,
|
|
12
14
|
typeToBitMask
|
|
@@ -24,15 +26,6 @@ import {Fee} from "./Fee.sol";
|
|
|
24
26
|
/// from the operation and inputs, enabling consistent state across chains.
|
|
25
27
|
abstract contract EncryptedOperations is IEncryptedOperations, BaseAccessControlList, HandleGeneration, Fee {
|
|
26
28
|
|
|
27
|
-
/// @notice Thrown when an operation receives a handle of an unexpected type.
|
|
28
|
-
/// @param actual The actual type of the handle.
|
|
29
|
-
/// @param expectedTypes A bitmask of the expected types.
|
|
30
|
-
error UnexpectedType(ETypes actual, bytes32 expectedTypes);
|
|
31
|
-
|
|
32
|
-
/// @notice Thrown when an operation receives an unsupported type.
|
|
33
|
-
/// @param actual The unsupported type.
|
|
34
|
-
error UnsupportedType(ETypes actual);
|
|
35
|
-
|
|
36
29
|
/// @notice Thrown when a cast is attempted to the same type.
|
|
37
30
|
/// @param t The type that was both source and target.
|
|
38
31
|
error SameTypeCast(ETypes t);
|
|
@@ -24,12 +24,12 @@ contract EListHandleMetadata is IEListHandleMetadata {
|
|
|
24
24
|
/// @notice Embeds the list length into a list handle
|
|
25
25
|
/// @dev Sets bytes 27-28 of the handle to the list length.
|
|
26
26
|
/// Clears existing length bits before setting new value.
|
|
27
|
-
/// @param
|
|
27
|
+
/// @param handle The 32-byte handle before length embedding
|
|
28
28
|
/// @param len The number of elements in the list (max 65535)
|
|
29
29
|
/// @return result The handle with embedded list length
|
|
30
|
-
function embedListLength(bytes32
|
|
30
|
+
function embedListLength(bytes32 handle, uint16 len) internal pure returns (bytes32 result) {
|
|
31
31
|
// 27 and 28 bits are used for the list length
|
|
32
|
-
result =
|
|
32
|
+
result = handle & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffff;
|
|
33
33
|
result = bytes32(uint256(result) | (uint256(len) << 24)); // append length
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -44,11 +44,11 @@ contract EListHandleMetadata is IEListHandleMetadata {
|
|
|
44
44
|
/// @notice Embeds the element type into a list handle
|
|
45
45
|
/// @dev Sets byte 29 of the handle to the element type.
|
|
46
46
|
/// This indicates the encrypted type of individual elements (euint8, euint64, etc.).
|
|
47
|
-
/// @param
|
|
47
|
+
/// @param handle The 32-byte handle before type embedding
|
|
48
48
|
/// @param listType The encrypted type of list elements
|
|
49
49
|
/// @return result The handle with embedded element type
|
|
50
|
-
function embedListType(bytes32
|
|
51
|
-
result =
|
|
50
|
+
function embedListType(bytes32 handle, ETypes listType) internal pure returns (bytes32 result) {
|
|
51
|
+
result = handle & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff;
|
|
52
52
|
result = bytes32(uint256(result) | (uint256(listType) << 16)); // append element type
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
EVM_HOST_CHAIN_PREFIX,
|
|
8
8
|
HANDLE_INDEX,
|
|
9
9
|
HANDLE_VERSION,
|
|
10
|
-
SEP_INPUT_PREHANDLE,
|
|
11
10
|
SEP_INPUT_HANDLE,
|
|
12
11
|
SEP_OP_RESULT
|
|
13
12
|
} from "../../Types.sol";
|
|
@@ -78,14 +77,6 @@ contract HandleGeneration is IHandleGeneration, HandleMetadata {
|
|
|
78
77
|
uint16 version,
|
|
79
78
|
ETypes inputType
|
|
80
79
|
) internal view returns (bytes32 generatedHandle) {
|
|
81
|
-
// Prehandle is a legacy concept inherited by an earlier design where we had a preflight system for uploading ciphertexts to an L1 before they could be used.
|
|
82
|
-
// This is no longer the case and prehandle was mainly kept for compatibility and consistency with the intrernal design docs.
|
|
83
|
-
// To avoid calling keccak256 twice and save gas the prehandle concept will be dropped in a future PR.
|
|
84
|
-
bytes32 ctIndexHash = keccak256(
|
|
85
|
-
// We don't encode the length of the ciphertext because it's the only variable length field which should prevent collisions.
|
|
86
|
-
abi.encodePacked(SEP_INPUT_PREHANDLE, keccak256(ciphertext), HANDLE_INDEX)
|
|
87
|
-
);
|
|
88
|
-
bytes32 prehandle = embedIndexTypeVersion(ctIndexHash, inputType);
|
|
89
80
|
// We must also propagate the handle metadata to the final handle
|
|
90
81
|
generatedHandle = embedIndexTypeVersion(
|
|
91
82
|
keccak256(
|
|
@@ -93,7 +84,9 @@ contract HandleGeneration is IHandleGeneration, HandleMetadata {
|
|
|
93
84
|
SEP_INPUT_HANDLE,
|
|
94
85
|
HANDLE_VERSION,
|
|
95
86
|
inputType,
|
|
96
|
-
|
|
87
|
+
uint32(ciphertext.length),
|
|
88
|
+
ciphertext,
|
|
89
|
+
HANDLE_INDEX,
|
|
97
90
|
EVM_HOST_CHAIN_PREFIX,
|
|
98
91
|
block.chainid, // todo cache this
|
|
99
92
|
executorAddress,
|
|
@@ -16,35 +16,35 @@ contract HandleMetadata {
|
|
|
16
16
|
/// @param raw The raw uint8 value extracted from the handle.
|
|
17
17
|
error InvalidTypeValue(uint8 raw);
|
|
18
18
|
|
|
19
|
-
/// @notice Embeds the handle index, encrypted type, and protocol version into a
|
|
19
|
+
/// @notice Embeds the handle index, encrypted type, and protocol version into a handle
|
|
20
20
|
/// @dev Used for input handles where the index distinguishes the source.
|
|
21
|
-
/// Clears bytes 29-31 of the
|
|
21
|
+
/// Clears bytes 29-31 of the handle, then sets:
|
|
22
22
|
/// - Byte 29: HANDLE_INDEX constant
|
|
23
23
|
/// - Byte 30: inputType enum value
|
|
24
24
|
/// - Byte 31: HANDLE_VERSION constant
|
|
25
|
-
/// @param
|
|
25
|
+
/// @param handle The 32-byte hash before metadata embedding
|
|
26
26
|
/// @param inputType The encrypted type to embed (ebool, euint8, etc.)
|
|
27
27
|
/// @return result The complete handle with embedded metadata
|
|
28
|
-
function embedIndexTypeVersion(bytes32
|
|
28
|
+
function embedIndexTypeVersion(bytes32 handle, ETypes inputType) internal pure returns (bytes32 result) {
|
|
29
29
|
// Create a mask to clear the last three bytes
|
|
30
30
|
bytes32 mask = bytes32(uint256(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFF));
|
|
31
31
|
// Clear the last three bytes of the original value
|
|
32
|
-
bytes32 clearedOriginal =
|
|
32
|
+
bytes32 clearedOriginal = handle & mask;
|
|
33
33
|
// Combine the cleared original value with the new last three bytes
|
|
34
34
|
result = clearedOriginal | bytes32((uint256(HANDLE_INDEX) << 16));
|
|
35
35
|
result = embedTypeVersion(result, inputType);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
/// @notice Embeds the encrypted type and protocol version into a
|
|
38
|
+
/// @notice Embeds the encrypted type and protocol version into a handle
|
|
39
39
|
/// @dev Used for operation result handles and trivial encryptions.
|
|
40
|
-
/// Clears bytes 30-31 of the
|
|
40
|
+
/// Clears bytes 30-31 of the handle, then sets:
|
|
41
41
|
/// - Byte 30: handleType enum value
|
|
42
42
|
/// - Byte 31: HANDLE_VERSION constant
|
|
43
|
-
/// @param
|
|
43
|
+
/// @param handle The 32-byte hash before metadata embedding
|
|
44
44
|
/// @param handleType The encrypted type to embed (ebool, euint8, etc.)
|
|
45
45
|
/// @return result The handle with type and version embedded
|
|
46
|
-
function embedTypeVersion(bytes32
|
|
47
|
-
result =
|
|
46
|
+
function embedTypeVersion(bytes32 handle, ETypes handleType) internal pure returns (bytes32 result) {
|
|
47
|
+
result = handle & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000;
|
|
48
48
|
result = bytes32(uint256(result) | (uint256(handleType) << 8)); // append type
|
|
49
49
|
result = bytes32(uint256(result) | HANDLE_VERSION);
|
|
50
50
|
}
|
|
@@ -16,7 +16,9 @@ import {
|
|
|
16
16
|
typeToBitMask,
|
|
17
17
|
EOps,
|
|
18
18
|
isTypeSupported,
|
|
19
|
-
SenderNotAllowedForHandle
|
|
19
|
+
SenderNotAllowedForHandle,
|
|
20
|
+
UnexpectedType,
|
|
21
|
+
UnsupportedType
|
|
20
22
|
} from "../../Types.sol";
|
|
21
23
|
import {VerifierAddressGetter} from "../primitives/VerifierAddressGetter.sol";
|
|
22
24
|
import {FEE} from "../Fee.sol";
|
|
@@ -77,11 +79,7 @@ contract TestHandleMetadata is
|
|
|
77
79
|
ebool control = this.asEbool(false);
|
|
78
80
|
euint256 a = this.asEuint256(42);
|
|
79
81
|
ebool b = this.asEbool(true);
|
|
80
|
-
vm.expectRevert(
|
|
81
|
-
abi.encodeWithSelector(
|
|
82
|
-
EncryptedOperations.UnexpectedType.selector, ETypes.Bool, typeToBitMask(ETypes.Uint256)
|
|
83
|
-
)
|
|
84
|
-
);
|
|
82
|
+
vm.expectRevert(abi.encodeWithSelector(UnexpectedType.selector, ETypes.Bool, typeToBitMask(ETypes.Uint256)));
|
|
85
83
|
this.eIfThenElse(control, euint256.unwrap(a), ebool.unwrap(b));
|
|
86
84
|
}
|
|
87
85
|
|
|
@@ -286,11 +284,7 @@ contract TestHandleMetadata is
|
|
|
286
284
|
ebool b = this.asEbool(true);
|
|
287
285
|
// Error: UnexpectedType(typeOf(rhs), typeToBitMask(lhsType))
|
|
288
286
|
// With lhs=Uint256, rhs=Bool: UnexpectedType(Bool, typeToBitMask(Uint256))
|
|
289
|
-
vm.expectRevert(
|
|
290
|
-
abi.encodeWithSelector(
|
|
291
|
-
EncryptedOperations.UnexpectedType.selector, ETypes.Bool, typeToBitMask(ETypes.Uint256)
|
|
292
|
-
)
|
|
293
|
-
);
|
|
287
|
+
vm.expectRevert(abi.encodeWithSelector(UnexpectedType.selector, ETypes.Bool, typeToBitMask(ETypes.Uint256)));
|
|
294
288
|
this.eBitAnd(euint256.unwrap(a), ebool.unwrap(b));
|
|
295
289
|
}
|
|
296
290
|
|
|
@@ -298,11 +292,7 @@ contract TestHandleMetadata is
|
|
|
298
292
|
function testEBitOrTypeMismatch() public {
|
|
299
293
|
euint256 a = this.asEuint256(42);
|
|
300
294
|
ebool b = this.asEbool(true);
|
|
301
|
-
vm.expectRevert(
|
|
302
|
-
abi.encodeWithSelector(
|
|
303
|
-
EncryptedOperations.UnexpectedType.selector, ETypes.Bool, typeToBitMask(ETypes.Uint256)
|
|
304
|
-
)
|
|
305
|
-
);
|
|
295
|
+
vm.expectRevert(abi.encodeWithSelector(UnexpectedType.selector, ETypes.Bool, typeToBitMask(ETypes.Uint256)));
|
|
306
296
|
this.eBitOr(euint256.unwrap(a), ebool.unwrap(b));
|
|
307
297
|
}
|
|
308
298
|
|
|
@@ -310,11 +300,7 @@ contract TestHandleMetadata is
|
|
|
310
300
|
function testEBitXorTypeMismatch() public {
|
|
311
301
|
euint256 a = this.asEuint256(42);
|
|
312
302
|
ebool b = this.asEbool(true);
|
|
313
|
-
vm.expectRevert(
|
|
314
|
-
abi.encodeWithSelector(
|
|
315
|
-
EncryptedOperations.UnexpectedType.selector, ETypes.Bool, typeToBitMask(ETypes.Uint256)
|
|
316
|
-
)
|
|
317
|
-
);
|
|
303
|
+
vm.expectRevert(abi.encodeWithSelector(UnexpectedType.selector, ETypes.Bool, typeToBitMask(ETypes.Uint256)));
|
|
318
304
|
this.eBitXor(euint256.unwrap(a), ebool.unwrap(b));
|
|
319
305
|
}
|
|
320
306
|
|
|
@@ -322,7 +308,7 @@ contract TestHandleMetadata is
|
|
|
322
308
|
function testECastUnsupportedType() public {
|
|
323
309
|
euint256 a = this.asEuint256(42);
|
|
324
310
|
ETypes unsupportedType = ETypes.Uint4UNSUPPORTED;
|
|
325
|
-
vm.expectRevert(abi.encodeWithSelector(
|
|
311
|
+
vm.expectRevert(abi.encodeWithSelector(UnsupportedType.selector, unsupportedType));
|
|
326
312
|
this.eCast(euint256.unwrap(a), unsupportedType);
|
|
327
313
|
}
|
|
328
314
|
|
|
@@ -330,7 +316,7 @@ contract TestHandleMetadata is
|
|
|
330
316
|
function testERandBoundedUnsupportedType() public {
|
|
331
317
|
euint256 bound = this.asEuint256(100);
|
|
332
318
|
ETypes unsupportedType = ETypes.Uint4UNSUPPORTED;
|
|
333
|
-
vm.expectRevert(abi.encodeWithSelector(
|
|
319
|
+
vm.expectRevert(abi.encodeWithSelector(UnsupportedType.selector, unsupportedType));
|
|
334
320
|
this.eRandBounded{value: FEE}(euint256.unwrap(bound), unsupportedType);
|
|
335
321
|
}
|
|
336
322
|
|
|
@@ -340,7 +326,7 @@ contract TestHandleMetadata is
|
|
|
340
326
|
// Create a handle with unsupported type by manually crafting one
|
|
341
327
|
bytes32 unsupportedHandle = embedIndexTypeVersion(bytes32(uint256(1)), ETypes.Uint4UNSUPPORTED);
|
|
342
328
|
ebool ifFalse = this.asEbool(false);
|
|
343
|
-
vm.expectRevert(abi.encodeWithSelector(
|
|
329
|
+
vm.expectRevert(abi.encodeWithSelector(UnsupportedType.selector, ETypes.Uint4UNSUPPORTED));
|
|
344
330
|
this.eIfThenElse(control, unsupportedHandle, ebool.unwrap(ifFalse));
|
|
345
331
|
}
|
|
346
332
|
|
|
@@ -351,11 +337,7 @@ contract TestHandleMetadata is
|
|
|
351
337
|
ebool ifFalse = this.asEbool(false);
|
|
352
338
|
// ifTrue is Uint256, ifFalse is Bool - type mismatch should trigger checkInput failure
|
|
353
339
|
// Error is UnexpectedType(ifFalse type=Bool, required type mask for Uint256)
|
|
354
|
-
vm.expectRevert(
|
|
355
|
-
abi.encodeWithSelector(
|
|
356
|
-
EncryptedOperations.UnexpectedType.selector, ETypes.Bool, typeToBitMask(ETypes.Uint256)
|
|
357
|
-
)
|
|
358
|
-
);
|
|
340
|
+
vm.expectRevert(abi.encodeWithSelector(UnexpectedType.selector, ETypes.Bool, typeToBitMask(ETypes.Uint256)));
|
|
359
341
|
this.eIfThenElse(control, euint256.unwrap(ifTrue), ebool.unwrap(ifFalse));
|
|
360
342
|
}
|
|
361
343
|
|
|
@@ -379,7 +361,7 @@ contract TestHandleMetadata is
|
|
|
379
361
|
// ============ Fuzz Tests for HandleMetadata ============
|
|
380
362
|
|
|
381
363
|
/// @dev Fuzz test for type embedding round-trip: typeOf(embedTypeVersion(h, t)) == t
|
|
382
|
-
function testFuzzTypeEmbeddingRoundTrip(bytes32
|
|
364
|
+
function testFuzzTypeEmbeddingRoundTrip(bytes32 handle, uint8 typeIndex) public pure {
|
|
383
365
|
// Constrain to supported types (0=Bool, 7=AddressOrUint160OrBytes20, 8=Uint256)
|
|
384
366
|
ETypes inputType;
|
|
385
367
|
uint8 typeSelector = typeIndex % 3;
|
|
@@ -391,14 +373,14 @@ contract TestHandleMetadata is
|
|
|
391
373
|
inputType = ETypes.Uint256;
|
|
392
374
|
}
|
|
393
375
|
|
|
394
|
-
bytes32 embeddedHandle = embedTypeVersion(
|
|
376
|
+
bytes32 embeddedHandle = embedTypeVersion(handle, inputType);
|
|
395
377
|
ETypes extractedType = typeOf(embeddedHandle);
|
|
396
378
|
|
|
397
379
|
assert(extractedType == inputType);
|
|
398
380
|
}
|
|
399
381
|
|
|
400
382
|
/// @dev Fuzz test for embedIndexTypeVersion round-trip
|
|
401
|
-
function testFuzzEmbedIndexTypeVersionRoundTrip(bytes32
|
|
383
|
+
function testFuzzEmbedIndexTypeVersionRoundTrip(bytes32 handle, uint8 typeIndex) public pure {
|
|
402
384
|
// Constrain to supported types
|
|
403
385
|
ETypes inputType;
|
|
404
386
|
uint8 typeSelector = typeIndex % 3;
|
|
@@ -410,14 +392,14 @@ contract TestHandleMetadata is
|
|
|
410
392
|
inputType = ETypes.Uint256;
|
|
411
393
|
}
|
|
412
394
|
|
|
413
|
-
bytes32 embeddedHandle = embedIndexTypeVersion(
|
|
395
|
+
bytes32 embeddedHandle = embedIndexTypeVersion(handle, inputType);
|
|
414
396
|
ETypes extractedType = typeOf(embeddedHandle);
|
|
415
397
|
|
|
416
398
|
assert(extractedType == inputType);
|
|
417
399
|
}
|
|
418
400
|
|
|
419
401
|
/// @dev Fuzz test that typeOf correctly extracts type from handles with valid embedded types
|
|
420
|
-
function testFuzzTypeOfExtraction(bytes32
|
|
402
|
+
function testFuzzTypeOfExtraction(bytes32 handle, uint8 typeIndex) public pure {
|
|
421
403
|
// First embed a valid type, then verify extraction
|
|
422
404
|
ETypes inputType;
|
|
423
405
|
uint8 typeSelector = typeIndex % 3;
|
|
@@ -429,7 +411,7 @@ contract TestHandleMetadata is
|
|
|
429
411
|
inputType = ETypes.Uint256;
|
|
430
412
|
}
|
|
431
413
|
|
|
432
|
-
bytes32 handle = embedTypeVersion(
|
|
414
|
+
bytes32 handle = embedTypeVersion(handle, inputType);
|
|
433
415
|
|
|
434
416
|
// Extract the type from the handle
|
|
435
417
|
ETypes extractedType = typeOf(handle);
|
|
@@ -444,7 +426,7 @@ contract TestHandleMetadata is
|
|
|
444
426
|
}
|
|
445
427
|
|
|
446
428
|
/// @dev Fuzz test that embedding preserves upper bits of handle
|
|
447
|
-
function testFuzzEmbeddingPreservesUpperBits(bytes32
|
|
429
|
+
function testFuzzEmbeddingPreservesUpperBits(bytes32 handle, uint8 typeIndex) public pure {
|
|
448
430
|
ETypes inputType;
|
|
449
431
|
uint8 typeSelector = typeIndex % 3;
|
|
450
432
|
if (typeSelector == 0) {
|
|
@@ -455,16 +437,16 @@ contract TestHandleMetadata is
|
|
|
455
437
|
inputType = ETypes.Uint256;
|
|
456
438
|
}
|
|
457
439
|
|
|
458
|
-
bytes32 embeddedHandle = embedTypeVersion(
|
|
440
|
+
bytes32 embeddedHandle = embedTypeVersion(handle, inputType);
|
|
459
441
|
|
|
460
442
|
// Verify upper 30 bytes (240 bits) are preserved
|
|
461
443
|
// Mask out the lower 2 bytes (16 bits) for comparison
|
|
462
444
|
bytes32 upperMask = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000;
|
|
463
|
-
assert((
|
|
445
|
+
assert((handle & upperMask) == (embeddedHandle & upperMask));
|
|
464
446
|
}
|
|
465
447
|
|
|
466
448
|
/// @dev Fuzz test that embedIndexTypeVersion clears index byte correctly
|
|
467
|
-
function testFuzzEmbedIndexClearsCorrectly(bytes32
|
|
449
|
+
function testFuzzEmbedIndexClearsCorrectly(bytes32 handle, uint8 typeIndex) public pure {
|
|
468
450
|
ETypes inputType;
|
|
469
451
|
uint8 typeSelector = typeIndex % 3;
|
|
470
452
|
if (typeSelector == 0) {
|
|
@@ -475,7 +457,7 @@ contract TestHandleMetadata is
|
|
|
475
457
|
inputType = ETypes.Uint256;
|
|
476
458
|
}
|
|
477
459
|
|
|
478
|
-
bytes32 embeddedHandle = embedIndexTypeVersion(
|
|
460
|
+
bytes32 embeddedHandle = embedIndexTypeVersion(handle, inputType);
|
|
479
461
|
|
|
480
462
|
// Extract index byte (bits 16-23) - should be HANDLE_INDEX (0)
|
|
481
463
|
uint8 indexByte = uint8(uint256(embeddedHandle) >> 16);
|
|
@@ -26,18 +26,21 @@ struct Session {
|
|
|
26
26
|
|
|
27
27
|
/// @title SessionVerifier
|
|
28
28
|
/// @notice Inco access sharing verifier for browser dApp sessions
|
|
29
|
-
/// @dev Grants
|
|
30
|
-
///
|
|
29
|
+
/// @dev Grants a single decrypter address temporary access to all of the sharer's
|
|
30
|
+
/// encrypted handles. The session is valid as long as block.timestamp < expiresAt
|
|
31
|
+
/// and the requesting account matches the authorized decrypter.
|
|
31
32
|
///
|
|
32
33
|
/// Usage:
|
|
33
|
-
/// 1. User signs a voucher containing a Session struct with their chosen decrypter
|
|
34
|
-
///
|
|
35
|
-
///
|
|
34
|
+
/// 1. User signs a voucher containing a Session struct with their chosen decrypter
|
|
35
|
+
/// and expiration time.
|
|
36
|
+
/// 2. The voucher specifies canUseSession.selector as the callFunction.
|
|
37
|
+
/// 3. When the decrypter requests access, this contract verifies the session is still
|
|
38
|
+
/// valid and the caller matches the authorized decrypter.
|
|
36
39
|
///
|
|
37
40
|
/// To use this verifier, set the voucher's callFunction to SessionVerifier.canUseSession.selector
|
|
38
41
|
contract SessionVerifier is UUPSUpgradeable, OwnableUpgradeable, Version {
|
|
39
42
|
|
|
40
|
-
/// @notice Initializes the SessionVerifier with version information
|
|
43
|
+
/// @notice Initializes the SessionVerifier with version information.
|
|
41
44
|
/// @param _salt Unique salt used for deterministic deployment via CreateX
|
|
42
45
|
constructor(bytes32 _salt)
|
|
43
46
|
Version(
|
|
@@ -49,29 +52,28 @@ contract SessionVerifier is UUPSUpgradeable, OwnableUpgradeable, Version {
|
|
|
49
52
|
)
|
|
50
53
|
{}
|
|
51
54
|
|
|
52
|
-
/// @notice Verifies if an account can use a session to access encrypted
|
|
55
|
+
/// @notice Verifies if an account can use a session to access an encrypted handle.
|
|
53
56
|
/// @dev This function is called by the ACL system when validating access permissions.
|
|
54
|
-
///
|
|
55
|
-
/// parameter is intentionally ignored.
|
|
57
|
+
/// Access is granted if the session has not expired and the caller is the authorized decrypter.
|
|
56
58
|
/// @param account The address requesting access (must match session.decrypter)
|
|
57
|
-
/// @param sharerArgData ABI-encoded Session struct containing decrypter
|
|
58
|
-
/// @return ALLOWANCE_GRANTED_MAGIC_VALUE if
|
|
59
|
+
/// @param sharerArgData ABI-encoded Session struct containing decrypter and expiration
|
|
60
|
+
/// @return ALLOWANCE_GRANTED_MAGIC_VALUE if access is granted, bytes32(0) otherwise
|
|
59
61
|
function canUseSession(
|
|
60
62
|
bytes32, /* handle */
|
|
61
63
|
address account,
|
|
62
64
|
bytes memory sharerArgData,
|
|
63
|
-
bytes memory requesterArgData
|
|
65
|
+
bytes memory /* requesterArgData */
|
|
64
66
|
)
|
|
65
67
|
external
|
|
66
68
|
view
|
|
67
69
|
returns (bytes32)
|
|
68
70
|
{
|
|
69
|
-
// unused variable just here to bypass linter
|
|
70
|
-
(requesterArgData);
|
|
71
71
|
Session memory session = abi.decode(sharerArgData, (Session));
|
|
72
|
+
|
|
72
73
|
if (session.expiresAt >= block.timestamp && session.decrypter == account) {
|
|
73
74
|
return ALLOWANCE_GRANTED_MAGIC_VALUE;
|
|
74
75
|
}
|
|
76
|
+
|
|
75
77
|
return bytes32(0);
|
|
76
78
|
}
|
|
77
79
|
|