@inco/lightning 0.9.0-devnet-test-4 → 0.10.0-devnet-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 +150 -0
- package/package.json +8 -8
- package/src/CreateXHelper.sol +1 -1
- package/src/DeployUtils.sol +22 -28
- package/src/IncoLightning.sol +27 -18
- package/src/IncoVerifier.sol +1 -1
- package/src/Lib.alphanet.sol +294 -13
- package/src/Lib.demonet.sol +294 -13
- package/src/Lib.devnet.sol +294 -13
- package/src/Lib.sol +294 -13
- package/src/Lib.template.sol +357 -13
- package/src/Lib.testnet.sol +294 -13
- package/src/Types.sol +44 -0
- package/src/interfaces/IIncoLightning.sol +8 -12
- package/src/interfaces/automata-interfaces/BELE.sol +1 -1
- package/src/interfaces/automata-interfaces/IPCCSRouter.sol +1 -1
- package/src/interfaces/automata-interfaces/IPcsDao.sol +1 -1
- package/src/interfaces/automata-interfaces/IQuoteVerifier.sol +1 -1
- package/src/interfaces/automata-interfaces/Types.sol +1 -1
- package/src/libs/incoLightning_alphanet_v0_297966649.sol +294 -13
- package/src/libs/incoLightning_alphanet_v1_725458969.sol +294 -13
- package/src/libs/incoLightning_alphanet_v2_976644394.sol +294 -13
- package/src/libs/incoLightning_demonet_v0_863421733.sol +294 -13
- package/src/libs/incoLightning_demonet_v2_467437523.sol +294 -13
- package/src/libs/incoLightning_devnet_v0_340846814.sol +294 -13
- package/src/libs/incoLightning_devnet_v10_266391127.sol +1223 -0
- package/src/libs/incoLightning_devnet_v1_904635675.sol +294 -13
- package/src/libs/incoLightning_devnet_v2_295237520.sol +294 -13
- package/src/libs/incoLightning_devnet_v3_976859633.sol +294 -13
- package/src/libs/incoLightning_devnet_v4_409204766.sol +294 -13
- package/src/libs/incoLightning_devnet_v5_203964628.sol +1223 -0
- package/src/libs/incoLightning_devnet_v6_281949651.sol +1223 -0
- package/src/libs/incoLightning_devnet_v7_24560427.sol +1223 -0
- package/src/libs/incoLightning_devnet_v8_985328058.sol +1223 -0
- package/src/libs/incoLightning_devnet_v9_269218568.sol +1223 -0
- package/src/libs/incoLightning_testnet_v0_183408998.sol +294 -13
- package/src/libs/incoLightning_testnet_v2_889158349.sol +294 -13
- package/src/lightning-parts/AccessControl/AdvancedAccessControl.sol +8 -3
- package/src/lightning-parts/AccessControl/BaseAccessControlList.sol +0 -16
- package/src/lightning-parts/AccessControl/interfaces/IAdvancedAccessControl.sol +1 -1
- package/src/lightning-parts/AccessControl/interfaces/IBaseAccessControlList.sol +3 -2
- package/src/lightning-parts/AccessControl/test/TestAdvancedAccessControl.t.sol +18 -1
- package/src/lightning-parts/AccessControl/test/TestBaseAccessControl.t.sol +0 -43
- package/src/lightning-parts/DecryptionAttester.sol +124 -2
- package/src/lightning-parts/DecryptionAttester.types.sol +20 -0
- package/src/lightning-parts/EList.sol +397 -0
- package/src/lightning-parts/EncryptedInput.sol +78 -8
- package/src/lightning-parts/EncryptedOperations.sol +40 -34
- package/src/lightning-parts/Fee.sol +29 -0
- package/src/lightning-parts/TEELifecycle.sol +38 -30
- package/src/lightning-parts/TEELifecycle.types.sol +1 -1
- package/src/lightning-parts/TrivialEncryption.sol +1 -2
- package/src/lightning-parts/interfaces/IDecryptionAttester.sol +16 -1
- package/src/lightning-parts/interfaces/IEList.sol +38 -0
- package/src/lightning-parts/interfaces/IEncryptedInput.sol +9 -1
- package/src/lightning-parts/interfaces/IEncryptedOperations.sol +3 -2
- package/src/lightning-parts/interfaces/ITEELifecycle.sol +1 -1
- package/src/lightning-parts/interfaces/ITrivialEncryption.sol +3 -1
- package/src/lightning-parts/primitives/EListHandleGeneration.sol +66 -0
- package/src/lightning-parts/primitives/EListHandleMetadata.sol +67 -0
- package/src/lightning-parts/primitives/HandleGeneration.sol +31 -8
- package/src/lightning-parts/primitives/HandleMetadata.sol +10 -3
- package/src/lightning-parts/primitives/interfaces/IEListHandleMetadata.sol +8 -0
- package/src/lightning-parts/primitives/test/SignatureVerifier.t.sol +1 -1
- package/src/lightning-parts/test/Elist.t.sol +218 -0
- package/src/lightning-parts/test/HandleMetadata.t.sol +66 -23
- package/src/lightning-parts/test/TestDecryptionAttestationInSynchronousFlow.t.sol +41 -13
- package/src/misc/ABIHelper.sol +15 -0
- package/src/pasted-dependencies/ICreateX.sol +1 -1
- package/src/periphery/IncoUtils.sol +1 -1
- package/src/periphery/SessionVerifier.sol +4 -4
- package/src/shared/IOwnable.sol +1 -1
- package/src/shared/IUUPSUpgradable.sol +1 -1
- package/src/shared/TestUtils.sol +8 -1
- package/src/test/EListTester.sol +171 -0
- package/src/test/FakeIncoInfra/FakeComputeServer.sol +2 -2
- package/src/test/FakeIncoInfra/FakeIncoInfraBase.sol +3 -3
- package/src/test/FakeIncoInfra/MockOpHandler.sol +7 -9
- package/src/test/FakeIncoInfra/MockRemoteAttestation.sol +2 -1
- package/src/test/FakeIncoInfra/getOpForSelector.sol +0 -2
- package/src/test/IncoTest.sol +17 -5
- package/src/test/OpsTest.sol +3 -2
- package/src/test/TEELifecycle/TEELifecycleMockTest.t.sol +85 -57
- package/src/test/TestDeploy.t.sol +73 -1
- package/src/test/TestFakeInfra.t.sol +32 -6
- package/src/test/TestLib.t.sol +986 -19
- package/src/test/TestReceive.t.sol +42 -0
- package/src/test/TestUpgrade.t.sol +34 -63
- package/src/version/IncoLightningConfig.sol +1 -1
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
// SPDX-License-Identifier: No License
|
|
2
2
|
pragma solidity ^0.8;
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
DecryptionAttestation,
|
|
6
|
+
ElementAttestationWithProof,
|
|
7
|
+
ReencryptionAttestation
|
|
8
|
+
} from "./DecryptionAttester.types.sol";
|
|
5
9
|
import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
|
|
10
|
+
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
6
11
|
import {SignatureVerifier} from "./primitives/SignatureVerifier.sol";
|
|
7
12
|
import {IDecryptionAttester} from "./interfaces/IDecryptionAttester.sol";
|
|
8
13
|
|
|
@@ -14,10 +19,20 @@ import {IDecryptionAttester} from "./interfaces/IDecryptionAttester.sol";
|
|
|
14
19
|
/// on-chain verification that a decryption was performed correctly by authorized covalidators.
|
|
15
20
|
abstract contract DecryptionAttester is IDecryptionAttester, SignatureVerifier, EIP712Upgradeable {
|
|
16
21
|
|
|
22
|
+
using ECDSA for bytes32;
|
|
23
|
+
|
|
24
|
+
/// @dev Thrown when attestations and signatures arrays have different lengths.
|
|
25
|
+
error AttestationsSignaturesLengthMismatch(uint256 attestationsLength, uint256 signaturesLength);
|
|
26
|
+
|
|
17
27
|
/// @dev EIP-712 type hash for DecryptionAttestation struct.
|
|
18
28
|
bytes32 constant DECRYPTION_ATTESTATION_STRUCT_HASH =
|
|
19
29
|
keccak256("DecryptionAttestation(bytes32 handle,bytes32 value)");
|
|
20
30
|
|
|
31
|
+
/// @dev EIP-712 type hash for Reencryption struct.
|
|
32
|
+
/// Matches the Go-side definition in eip712.go: Reencryption(bytes user_ciphertext,bytes32 handle,bytes signature)
|
|
33
|
+
bytes32 constant REENCRYPTION_ATTESTATION_STRUCT_HASH =
|
|
34
|
+
keccak256("Reencryption(bytes user_ciphertext,bytes32 handle,bytes signature)");
|
|
35
|
+
|
|
21
36
|
/// @notice Computes the EIP-712 digest for a decryption attestation.
|
|
22
37
|
/// @dev Used to verify signatures from covalidators attesting to a decryption result.
|
|
23
38
|
/// @param decryption The decryption attestation containing the handle and decrypted value.
|
|
@@ -28,12 +43,28 @@ abstract contract DecryptionAttester is IDecryptionAttester, SignatureVerifier,
|
|
|
28
43
|
);
|
|
29
44
|
}
|
|
30
45
|
|
|
46
|
+
/// @notice Computes the EIP-712 digest for a reencryption attestation.
|
|
47
|
+
/// @param attestation The reencryption attestation containing handle, userCiphertext, and encryptedSignature.
|
|
48
|
+
/// @return The EIP-712 typed data hash.
|
|
49
|
+
function reencryptionAttestationDigest(ReencryptionAttestation memory attestation) public view returns (bytes32) {
|
|
50
|
+
return _hashTypedDataV4(
|
|
51
|
+
keccak256(
|
|
52
|
+
abi.encode(
|
|
53
|
+
REENCRYPTION_ATTESTATION_STRUCT_HASH,
|
|
54
|
+
keccak256(attestation.userCiphertext),
|
|
55
|
+
attestation.handle,
|
|
56
|
+
keccak256(attestation.encryptedSignature)
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
31
62
|
/// @notice Validates a decryption attestation against covalidator signatures.
|
|
32
63
|
/// @dev Verifies that the attestation was signed by the required threshold of covalidators.
|
|
33
64
|
/// @param decryption The decryption attestation to validate.
|
|
34
65
|
/// @param signatures Array of signatures from covalidators.
|
|
35
66
|
/// @return True if the attestation has valid signatures from the required covalidators.
|
|
36
|
-
function isValidDecryptionAttestation(DecryptionAttestation memory decryption, bytes[]
|
|
67
|
+
function isValidDecryptionAttestation(DecryptionAttestation memory decryption, bytes[] calldata signatures)
|
|
37
68
|
public
|
|
38
69
|
view
|
|
39
70
|
returns (bool)
|
|
@@ -41,4 +72,95 @@ abstract contract DecryptionAttester is IDecryptionAttester, SignatureVerifier,
|
|
|
41
72
|
return isValidSignature(decryptionAttestationDigest(decryption), signatures);
|
|
42
73
|
}
|
|
43
74
|
|
|
75
|
+
/// @notice Validates an elist decryption attestation with commitment proof against covalidator signatures.
|
|
76
|
+
/// @dev Verifies that the attestation was signed by the required threshold of covalidators and validates the proof construction.
|
|
77
|
+
/// Each element can be either a pre-computed hash or a commitment-value pair.
|
|
78
|
+
/// @param elistHandle The elist handle that was decrypted
|
|
79
|
+
/// @param proofElements Array of proved values. Each can provide either pairHash directly, or commitment+value to compute the pairHash
|
|
80
|
+
/// @param proof The expected proof that should match keccak256 of all concatenated pair hashes
|
|
81
|
+
/// @param signatures Array of signatures from covalidators
|
|
82
|
+
/// @return bool True if the attestation has valid signatures and proof verification succeeds
|
|
83
|
+
function isValidEListDecryptionAttestation(
|
|
84
|
+
bytes32 elistHandle,
|
|
85
|
+
ElementAttestationWithProof[] calldata proofElements,
|
|
86
|
+
bytes32 proof,
|
|
87
|
+
bytes[] calldata signatures
|
|
88
|
+
) public view override returns (bool) {
|
|
89
|
+
DecryptionAttestation memory attestation = DecryptionAttestation({handle: elistHandle, value: proof});
|
|
90
|
+
|
|
91
|
+
// Verify the provided proof by reconstructing it from the provided value-commitment pairs and/or hashes.
|
|
92
|
+
bytes32[] memory elementHashes = new bytes32[](proofElements.length);
|
|
93
|
+
for (uint256 i = 0; i < proofElements.length; i++) {
|
|
94
|
+
// Use pre-computed hash if provided, otherwise compute from commitment and value
|
|
95
|
+
if (proofElements[i].pairHash != bytes32(0)) {
|
|
96
|
+
elementHashes[i] = proofElements[i].pairHash;
|
|
97
|
+
} else {
|
|
98
|
+
elementHashes[i] = keccak256(abi.encodePacked(proofElements[i].commitment, proofElements[i].value));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Single allocation: encode all hashes at once
|
|
103
|
+
bytes32 computedProof = keccak256(abi.encodePacked(elementHashes));
|
|
104
|
+
if (computedProof != proof) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return isValidDecryptionAttestation(attestation, signatures);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// @notice Validates reencryption attestations from multiple covalidators against the threshold.
|
|
112
|
+
/// @dev Each covalidator produces a different ciphertext (X-Wing encryption is randomized),
|
|
113
|
+
/// so each attestation has a unique digest. Each is verified individually and the function
|
|
114
|
+
/// checks that at least `threshold` unique authorized signers attested.
|
|
115
|
+
/// @param attestations Array of reencryption attestations, one per covalidator.
|
|
116
|
+
/// @param signatures Array of envelope signatures, one per attestation (same order).
|
|
117
|
+
/// @return True if at least `threshold` unique authorized covalidators signed valid attestations.
|
|
118
|
+
function isValidReencryptionAttestation(
|
|
119
|
+
ReencryptionAttestation[] calldata attestations,
|
|
120
|
+
bytes[] calldata signatures
|
|
121
|
+
) public view returns (bool) {
|
|
122
|
+
if (attestations.length != signatures.length) {
|
|
123
|
+
revert AttestationsSignaturesLengthMismatch(attestations.length, signatures.length);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
uint256 threshold = getThreshold();
|
|
127
|
+
if (threshold == 0 || attestations.length < threshold) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
address[] memory recoveredSigners = new address[](attestations.length);
|
|
132
|
+
address lastSigner = address(0);
|
|
133
|
+
uint256 correctSignaturesCount = 0;
|
|
134
|
+
uint256 validCount = 0;
|
|
135
|
+
|
|
136
|
+
for (uint256 i = 0; i < attestations.length && correctSignaturesCount < threshold; i++) {
|
|
137
|
+
bytes32 digest = reencryptionAttestationDigest(attestations[i]);
|
|
138
|
+
(address currentSigner, ECDSA.RecoverError err,) = digest.tryRecover(signatures[i]);
|
|
139
|
+
|
|
140
|
+
if (err != ECDSA.RecoverError.NoError) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Duplicate detection (same pattern as SignatureVerifier.isValidSignature)
|
|
145
|
+
if (currentSigner > lastSigner) {
|
|
146
|
+
lastSigner = currentSigner;
|
|
147
|
+
} else {
|
|
148
|
+
for (uint256 j = 0; j < validCount; ++j) {
|
|
149
|
+
if (currentSigner == recoveredSigners[j]) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
recoveredSigners[validCount] = currentSigner;
|
|
156
|
+
validCount++;
|
|
157
|
+
|
|
158
|
+
if (isSigner(currentSigner)) {
|
|
159
|
+
correctSignaturesCount++;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return correctSignaturesCount >= threshold;
|
|
164
|
+
}
|
|
165
|
+
|
|
44
166
|
}
|
|
@@ -5,3 +5,23 @@ struct DecryptionAttestation {
|
|
|
5
5
|
bytes32 handle;
|
|
6
6
|
bytes32 value; // encoded bool or uint256 // todo switch this to bytes
|
|
7
7
|
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @notice Represents a decrypted elist value that can be either a pre-computed hash or a commitment-value pair
|
|
11
|
+
* @param pairHash Pre-computed keccak256(abi.encodePacked(commitment, value)). If non-zero, this is used directly
|
|
12
|
+
* @param commitment Commitment value. Only used if pairHash is zero
|
|
13
|
+
* @param value Decrypted value (encoded as bytes32 in Solidity). Only used if pairHash is zero
|
|
14
|
+
* @dev If pairHash is provided (non-zero), commitment and value are ignored. Otherwise, hash is computed as
|
|
15
|
+
* keccak256(abi.encodePacked(commitment, value)) using Solidity's packed ABI encoding
|
|
16
|
+
*/
|
|
17
|
+
struct ElementAttestationWithProof {
|
|
18
|
+
bytes32 pairHash;
|
|
19
|
+
bytes32 commitment;
|
|
20
|
+
bytes32 value;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
struct ReencryptionAttestation {
|
|
24
|
+
bytes32 handle;
|
|
25
|
+
bytes userCiphertext;
|
|
26
|
+
bytes encryptedSignature;
|
|
27
|
+
}
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
// SPDX-License-Identifier: No License
|
|
2
|
+
pragma solidity ^0.8;
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ETypes,
|
|
6
|
+
EOps,
|
|
7
|
+
typeToBitMask,
|
|
8
|
+
typeBitSize,
|
|
9
|
+
isTypeSupported,
|
|
10
|
+
IndexOutOfRange,
|
|
11
|
+
InvalidRange,
|
|
12
|
+
ListTypeMismatch,
|
|
13
|
+
ListTooLong,
|
|
14
|
+
UnsupportedType,
|
|
15
|
+
elist
|
|
16
|
+
} from "../Types.sol";
|
|
17
|
+
import {EncryptedOperations} from "./EncryptedOperations.sol";
|
|
18
|
+
import {EncryptedInput} from "./EncryptedInput.sol";
|
|
19
|
+
import {EListHandleGeneration} from "./primitives/EListHandleGeneration.sol";
|
|
20
|
+
import {IEList} from "./interfaces/IEList.sol";
|
|
21
|
+
|
|
22
|
+
/// @dev Maximum number of elements allowed in an encrypted list.
|
|
23
|
+
uint16 constant MAX_LIST_LENGTH = type(uint16).max;
|
|
24
|
+
|
|
25
|
+
/// @title EList
|
|
26
|
+
/// @notice Provides operations for encrypted lists (elist) - ordered collections of encrypted values.
|
|
27
|
+
/// @dev Encrypted lists are immutable; all operations return new list handles rather than modifying in place.
|
|
28
|
+
/// Lists are homogeneous - all elements must be of the same encrypted type. Operations are processed
|
|
29
|
+
/// by the covalidator off-chain.
|
|
30
|
+
abstract contract EList is IEList, EncryptedOperations, EncryptedInput, EListHandleGeneration {
|
|
31
|
+
|
|
32
|
+
event NewEList(bytes32 indexed result, ETypes listType, bytes32[] handles, uint256 eventId);
|
|
33
|
+
event EListAppend(elist indexed list, bytes32 indexed value, elist indexed result, uint256 eventId);
|
|
34
|
+
event EListGet(elist indexed list, uint16 indexed index, bytes32 indexed result, uint256 eventId);
|
|
35
|
+
event EListGetOr(
|
|
36
|
+
elist indexed list, bytes32 index, bytes32 indexed defaultValue, bytes32 indexed result, uint256 eventId
|
|
37
|
+
);
|
|
38
|
+
event EListSet(elist list, bytes32 indexed index, bytes32 indexed value, elist indexed result, uint256 eventId);
|
|
39
|
+
event EListInsert(elist list, bytes32 indexed index, bytes32 indexed value, elist indexed result, uint256 eventId);
|
|
40
|
+
event EListConcat(elist indexed list1, elist indexed list2, elist indexed result, uint256 eventId);
|
|
41
|
+
event EListSlice(
|
|
42
|
+
elist list,
|
|
43
|
+
bytes32 indexed start,
|
|
44
|
+
uint16 length,
|
|
45
|
+
bytes32 indexed defaultValue,
|
|
46
|
+
elist indexed result,
|
|
47
|
+
uint256 eventId
|
|
48
|
+
);
|
|
49
|
+
event EListRange(
|
|
50
|
+
uint256 indexed start, uint256 indexed end, ETypes listType, elist indexed result, uint256 eventId
|
|
51
|
+
);
|
|
52
|
+
event EListShuffle(elist indexed list, uint256 indexed counter, elist indexed result, uint256 eventId);
|
|
53
|
+
event EListReverse(elist indexed list, elist indexed result, uint256 eventId);
|
|
54
|
+
|
|
55
|
+
/// @dev Returns the total bit size of an elist: length * typeBitSize(elementType).
|
|
56
|
+
function _elistBits(bytes32 handle) internal pure returns (uint256) {
|
|
57
|
+
return uint256(lengthOf(handle)) * typeBitSize(listTypeOf(handle));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// @notice Creates a new encrypted list from client-encrypted inputs.
|
|
61
|
+
/// @dev Internal function that processes multiple encrypted inputs without individual payment.
|
|
62
|
+
/// Payment should be handled by the caller for the batch.
|
|
63
|
+
/// @param inputs Array of encrypted inputs with prepended handles.
|
|
64
|
+
/// @param listType The type of elements in the list.
|
|
65
|
+
/// @param user The user address that encrypted the values.
|
|
66
|
+
/// @return newList The new encrypted list handle.
|
|
67
|
+
function newEListFromInputs(bytes[] calldata inputs, ETypes listType, address user)
|
|
68
|
+
internal
|
|
69
|
+
returns (elist newList)
|
|
70
|
+
{
|
|
71
|
+
require(inputs.length <= MAX_LIST_LENGTH, ListTooLong(uint32(inputs.length), MAX_LIST_LENGTH));
|
|
72
|
+
require(isTypeSupported(listType), UnsupportedType(listType));
|
|
73
|
+
|
|
74
|
+
// TODO: Add a new event to create new elist from inputs, can be done as an upgrade to optimize for gas and castore.
|
|
75
|
+
bytes32[] memory handles = new bytes32[](inputs.length);
|
|
76
|
+
for (uint256 i = 0; i < inputs.length; i++) {
|
|
77
|
+
// we check payment for multiple inputs ahead of this func
|
|
78
|
+
handles[i] = newInputNotPayingNotEmitting(inputs[i], user, listType);
|
|
79
|
+
}
|
|
80
|
+
return newEListFromHandles(handles, listType);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// @notice Creates a new encrypted list from existing encrypted handles.
|
|
84
|
+
/// @dev Validates that all handles are of the expected type and caller has access.
|
|
85
|
+
/// @param handles Array of encrypted value handles to include in the list.
|
|
86
|
+
/// @param listType The type of elements in the list (must match handle types).
|
|
87
|
+
/// @return newList The new encrypted list handle.
|
|
88
|
+
function newEListFromHandles(bytes32[] memory handles, ETypes listType) internal returns (elist newList) {
|
|
89
|
+
require(handles.length <= MAX_LIST_LENGTH, ListTooLong(uint32(handles.length), MAX_LIST_LENGTH));
|
|
90
|
+
require(isTypeSupported(listType), UnsupportedType(listType));
|
|
91
|
+
for (uint256 i = 0; i < handles.length; i++) {
|
|
92
|
+
checkInput(handles[i], typeToBitMask(listType));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
bytes32 newHandle = createListInputHandle(handles, listType);
|
|
96
|
+
|
|
97
|
+
allowTransientInternal(newHandle, msg.sender);
|
|
98
|
+
uint256 id = getNextEventId();
|
|
99
|
+
setDigest(abi.encodePacked(newHandle, id));
|
|
100
|
+
emit NewEList(newHandle, listType, handles, id);
|
|
101
|
+
return elist.wrap(newHandle);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// @notice Creates a new encrypted list from existing encrypted handles.
|
|
105
|
+
/// @dev External wrapper for newEListFromHandles.
|
|
106
|
+
/// @param handles Array of encrypted value handles to include in the list.
|
|
107
|
+
/// @param listType The type of elements in the list.
|
|
108
|
+
/// @return newList The new encrypted list handle.
|
|
109
|
+
function newEList(bytes32[] memory handles, ETypes listType)
|
|
110
|
+
external
|
|
111
|
+
payable
|
|
112
|
+
payingElistFee(uint256(handles.length) * typeBitSize(listType))
|
|
113
|
+
returns (elist newList)
|
|
114
|
+
{
|
|
115
|
+
return newEListFromHandles(handles, listType);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/// @notice Creates a new encrypted list from client-encrypted inputs.
|
|
119
|
+
/// @dev This is a paid operation. Payment scales with the number of inputs.
|
|
120
|
+
/// @param inputs Array of encrypted inputs with prepended handles.
|
|
121
|
+
/// @param listType The type of elements in the list.
|
|
122
|
+
/// @param user The user address that encrypted the values.
|
|
123
|
+
/// @return newList The new encrypted list handle.
|
|
124
|
+
function newEList(bytes[] calldata inputs, ETypes listType, address user)
|
|
125
|
+
external
|
|
126
|
+
payable
|
|
127
|
+
payingElistFee(uint256(inputs.length) * typeBitSize(listType))
|
|
128
|
+
returns (elist newList)
|
|
129
|
+
{
|
|
130
|
+
return newEListFromInputs(inputs, listType, user);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// @notice Appends an encrypted value to the end of an encrypted list.
|
|
134
|
+
/// @dev Returns a new list with the value appended; original list is unchanged.
|
|
135
|
+
/// @param list The encrypted list to append to.
|
|
136
|
+
/// @param value The encrypted value to append (must match list element type).
|
|
137
|
+
/// @return result A new encrypted list with the value appended.
|
|
138
|
+
function listAppend(elist list, bytes32 value)
|
|
139
|
+
external
|
|
140
|
+
payable
|
|
141
|
+
payingElistFee(_elistBits(elist.unwrap(list)) + typeBitSize(listTypeOf(elist.unwrap(list))))
|
|
142
|
+
returns (elist result)
|
|
143
|
+
{
|
|
144
|
+
checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
|
|
145
|
+
checkInput(value, typeToBitMask(listTypeOf(elist.unwrap(list))));
|
|
146
|
+
|
|
147
|
+
result = elist.wrap(
|
|
148
|
+
createListResultHandle(
|
|
149
|
+
EOps.EListAppend,
|
|
150
|
+
listTypeOf(elist.unwrap(list)),
|
|
151
|
+
lengthOf(elist.unwrap(list)) + 1,
|
|
152
|
+
abi.encodePacked(elist.unwrap(list), value)
|
|
153
|
+
)
|
|
154
|
+
);
|
|
155
|
+
uint256 id = getNextEventId();
|
|
156
|
+
allowTransientInternal(elist.unwrap(result), msg.sender);
|
|
157
|
+
setDigest(abi.encodePacked(elist.unwrap(result), id));
|
|
158
|
+
emit EListAppend(list, value, result, id);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/// @notice Retrieves an encrypted element at a specific index.
|
|
162
|
+
/// @dev Reverts if the index is out of range. For safe access with a default, use listGetOr.
|
|
163
|
+
/// @param list The encrypted list to access.
|
|
164
|
+
/// @param i The index to retrieve (0-based).
|
|
165
|
+
/// @return result The encrypted element at the specified index.
|
|
166
|
+
function listGet(elist list, uint16 i) external returns (bytes32 result) {
|
|
167
|
+
require(i < lengthOf(elist.unwrap(list)), IndexOutOfRange(i, lengthOf(elist.unwrap(list))));
|
|
168
|
+
checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
|
|
169
|
+
|
|
170
|
+
result =
|
|
171
|
+
createResultHandle(EOps.EListGet, listTypeOf(elist.unwrap(list)), abi.encodePacked(elist.unwrap(list), i));
|
|
172
|
+
uint256 id = getNextEventId();
|
|
173
|
+
setDigest(abi.encodePacked(result, id));
|
|
174
|
+
emit EListGet(list, i, result, id);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/// @notice Retrieves an encrypted element at an encrypted index, with a default value for out-of-range access.
|
|
178
|
+
/// @dev Returns the default value if the index is out of range. Index must be euint256.
|
|
179
|
+
/// @param list The encrypted list to access.
|
|
180
|
+
/// @param index The encrypted index to retrieve.
|
|
181
|
+
/// @param defaultValue The encrypted value to return if index is out of range.
|
|
182
|
+
/// @return result The encrypted element at the index, or defaultValue if out of range.
|
|
183
|
+
function listGetOr(elist list, bytes32 index, bytes32 defaultValue) external returns (bytes32 result) {
|
|
184
|
+
checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
|
|
185
|
+
checkInput(defaultValue, typeToBitMask(listTypeOf(elist.unwrap(list))));
|
|
186
|
+
checkInput(index, typeToBitMask(ETypes.Uint256)); //Currently we only support euint256 for index
|
|
187
|
+
|
|
188
|
+
result = createResultHandle(
|
|
189
|
+
EOps.EListGetOr, listTypeOf(elist.unwrap(list)), abi.encodePacked(elist.unwrap(list), index, defaultValue)
|
|
190
|
+
);
|
|
191
|
+
uint256 id = getNextEventId();
|
|
192
|
+
setDigest(abi.encodePacked(result, id));
|
|
193
|
+
emit EListGetOr(list, index, defaultValue, result, id);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/// @notice Sets an encrypted element at an encrypted index.
|
|
197
|
+
/// @dev Returns a new list with the element replaced; original list is unchanged.
|
|
198
|
+
/// Index must be euint256. If the encrypted index is out of bounds, the operation
|
|
199
|
+
/// is silently ignored by the covalidator and the returned list is identical to the input.
|
|
200
|
+
/// This is by design: because the index is encrypted, reverting would leak information
|
|
201
|
+
/// about whether the index was valid.
|
|
202
|
+
/// @param list The encrypted list to modify.
|
|
203
|
+
/// @param index The encrypted index to set.
|
|
204
|
+
/// @param value The new encrypted value (must match list element type).
|
|
205
|
+
/// @return result A new encrypted list with the element replaced, or unchanged if index is out of bounds.
|
|
206
|
+
function listSet(elist list, bytes32 index, bytes32 value)
|
|
207
|
+
external
|
|
208
|
+
payable
|
|
209
|
+
payingElistFee(_elistBits(elist.unwrap(list)))
|
|
210
|
+
returns (elist result)
|
|
211
|
+
{
|
|
212
|
+
checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
|
|
213
|
+
checkInput(index, typeToBitMask(ETypes.Uint256)); //Currently we only support euint256 for index
|
|
214
|
+
checkInput(value, typeToBitMask(listTypeOf(elist.unwrap(list))));
|
|
215
|
+
|
|
216
|
+
result = elist.wrap(
|
|
217
|
+
createListResultHandle(
|
|
218
|
+
EOps.EListSet,
|
|
219
|
+
listTypeOf(elist.unwrap(list)),
|
|
220
|
+
lengthOf(elist.unwrap(list)),
|
|
221
|
+
abi.encodePacked(elist.unwrap(list), index, value)
|
|
222
|
+
)
|
|
223
|
+
);
|
|
224
|
+
allowTransientInternal(elist.unwrap(result), msg.sender);
|
|
225
|
+
uint256 id = getNextEventId();
|
|
226
|
+
setDigest(abi.encodePacked(elist.unwrap(result), id));
|
|
227
|
+
emit EListSet(list, index, value, result, id);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/// @notice Inserts an encrypted element at an encrypted index, shifting subsequent elements.
|
|
231
|
+
/// @dev Returns a new list with one additional element. Index must be euint256.
|
|
232
|
+
/// @param list The encrypted list to modify.
|
|
233
|
+
/// @param index The encrypted index at which to insert.
|
|
234
|
+
/// @param value The encrypted value to insert (must match list element type).
|
|
235
|
+
/// @return result A new encrypted list with the element inserted.
|
|
236
|
+
function listInsert(elist list, bytes32 index, bytes32 value)
|
|
237
|
+
external
|
|
238
|
+
payable
|
|
239
|
+
payingElistFee(_elistBits(elist.unwrap(list)) + typeBitSize(listTypeOf(elist.unwrap(list))))
|
|
240
|
+
returns (elist result)
|
|
241
|
+
{
|
|
242
|
+
checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
|
|
243
|
+
checkInput(index, typeToBitMask(ETypes.Uint256)); //Currently we only support euint256 for index
|
|
244
|
+
checkInput(value, typeToBitMask(listTypeOf(elist.unwrap(list))));
|
|
245
|
+
|
|
246
|
+
result = elist.wrap(
|
|
247
|
+
createListResultHandle(
|
|
248
|
+
EOps.EListInsert,
|
|
249
|
+
listTypeOf(elist.unwrap(list)),
|
|
250
|
+
lengthOf(elist.unwrap(list)) + 1,
|
|
251
|
+
abi.encodePacked(elist.unwrap(list), index, value)
|
|
252
|
+
)
|
|
253
|
+
);
|
|
254
|
+
allowTransientInternal(elist.unwrap(result), msg.sender);
|
|
255
|
+
uint256 id = getNextEventId();
|
|
256
|
+
setDigest(abi.encodePacked(elist.unwrap(result), id));
|
|
257
|
+
emit EListInsert(list, index, value, result, id);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/// @notice Concatenates two encrypted lists into a new list.
|
|
261
|
+
/// @dev Both lists must have the same element type. Returns a new list with all elements.
|
|
262
|
+
/// @param lhs The first encrypted list.
|
|
263
|
+
/// @param rhs The second encrypted list (must have same element type as lhs).
|
|
264
|
+
/// @return result A new encrypted list containing all elements from both lists.
|
|
265
|
+
function listConcat(elist lhs, elist rhs)
|
|
266
|
+
external
|
|
267
|
+
payable
|
|
268
|
+
payingElistFee(_elistBits(elist.unwrap(lhs)) + _elistBits(elist.unwrap(rhs)))
|
|
269
|
+
returns (elist result)
|
|
270
|
+
{
|
|
271
|
+
checkInput(elist.unwrap(lhs), typeToBitMask(ETypes.List));
|
|
272
|
+
checkInput(elist.unwrap(rhs), typeToBitMask(ETypes.List));
|
|
273
|
+
ETypes lhsType = listTypeOf(elist.unwrap(lhs));
|
|
274
|
+
ETypes rhsType = listTypeOf(elist.unwrap(rhs));
|
|
275
|
+
require(lhsType == rhsType, ListTypeMismatch(lhsType, rhsType));
|
|
276
|
+
|
|
277
|
+
result = elist.wrap(
|
|
278
|
+
createListResultHandle(
|
|
279
|
+
EOps.EListConcat,
|
|
280
|
+
listTypeOf(elist.unwrap(lhs)),
|
|
281
|
+
lengthOf(elist.unwrap(lhs)) + lengthOf(elist.unwrap(rhs)),
|
|
282
|
+
abi.encodePacked(elist.unwrap(lhs), elist.unwrap(rhs))
|
|
283
|
+
)
|
|
284
|
+
);
|
|
285
|
+
allowTransientInternal(elist.unwrap(result), msg.sender);
|
|
286
|
+
uint256 id = getNextEventId();
|
|
287
|
+
setDigest(abi.encodePacked(elist.unwrap(result), id));
|
|
288
|
+
emit EListConcat(lhs, rhs, result, id);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/// @notice Extracts a slice from an encrypted list starting at an encrypted index.
|
|
292
|
+
/// @dev Returns a new list of the specified length. Uses defaultValue for out-of-range positions.
|
|
293
|
+
/// @param list The encrypted list to slice.
|
|
294
|
+
/// @param start The encrypted starting index.
|
|
295
|
+
/// @param len The number of elements to include in the slice.
|
|
296
|
+
/// @param defaultValue The encrypted value to use for out-of-range positions.
|
|
297
|
+
/// @return result A new encrypted list containing the slice.
|
|
298
|
+
function listSlice(elist list, bytes32 start, uint16 len, bytes32 defaultValue)
|
|
299
|
+
external
|
|
300
|
+
payable
|
|
301
|
+
payingElistFee(uint256(len) * typeBitSize(listTypeOf(elist.unwrap(list))))
|
|
302
|
+
returns (elist result)
|
|
303
|
+
{
|
|
304
|
+
checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
|
|
305
|
+
checkInput(defaultValue, typeToBitMask(listTypeOf(elist.unwrap(list))));
|
|
306
|
+
checkInput(start, typeToBitMask(ETypes.Uint256));
|
|
307
|
+
|
|
308
|
+
result = elist.wrap(
|
|
309
|
+
createListResultHandle(
|
|
310
|
+
EOps.EListSlice,
|
|
311
|
+
listTypeOf(elist.unwrap(list)),
|
|
312
|
+
len,
|
|
313
|
+
abi.encodePacked(elist.unwrap(list), start, defaultValue)
|
|
314
|
+
)
|
|
315
|
+
);
|
|
316
|
+
allowTransientInternal(elist.unwrap(result), msg.sender);
|
|
317
|
+
uint256 id = getNextEventId();
|
|
318
|
+
setDigest(abi.encodePacked(elist.unwrap(result), id));
|
|
319
|
+
emit EListSlice(list, start, len, defaultValue, result, id);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/// @notice Creates an encrypted list containing a range of encrypted integers.
|
|
323
|
+
/// @dev Creates a list of euint256 values from start (inclusive) to end (exclusive).
|
|
324
|
+
/// @param start The starting value (inclusive).
|
|
325
|
+
/// @param end The ending value (exclusive). Must be >= start.
|
|
326
|
+
/// @param listType The type of elements in the list.
|
|
327
|
+
/// @return result A new encrypted list containing the range [start, end).
|
|
328
|
+
function listRange(uint16 start, uint16 end, ETypes listType)
|
|
329
|
+
external
|
|
330
|
+
payable
|
|
331
|
+
payingElistFee(uint256(end - start) * typeBitSize(listType))
|
|
332
|
+
returns (elist result)
|
|
333
|
+
{
|
|
334
|
+
require(start <= end, InvalidRange(start, end));
|
|
335
|
+
|
|
336
|
+
result =
|
|
337
|
+
elist.wrap(createListResultHandle(EOps.EListRange, listType, end - start, abi.encodePacked(start, end)));
|
|
338
|
+
allowTransientInternal(elist.unwrap(result), msg.sender);
|
|
339
|
+
uint256 id = getNextEventId();
|
|
340
|
+
setDigest(abi.encodePacked(elist.unwrap(result), id));
|
|
341
|
+
emit EListRange(start, end, listType, result, id);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/// @notice Randomly shuffles the elements of an encrypted list.
|
|
345
|
+
/// @dev This is a paid operation. Returns a new list with elements in random order.
|
|
346
|
+
/// The shuffle is cryptographically secure, computed by the covalidator.
|
|
347
|
+
/// @param list The encrypted list to shuffle.
|
|
348
|
+
/// @return result A new encrypted list with elements in random order.
|
|
349
|
+
function listShuffle(elist list)
|
|
350
|
+
external
|
|
351
|
+
payable
|
|
352
|
+
payingElistFee(_elistBits(elist.unwrap(list)))
|
|
353
|
+
returns (elist result)
|
|
354
|
+
{
|
|
355
|
+
checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
|
|
356
|
+
randCounter++;
|
|
357
|
+
result = elist.wrap(
|
|
358
|
+
createListResultHandle(
|
|
359
|
+
EOps.EListShuffle,
|
|
360
|
+
listTypeOf(elist.unwrap(list)),
|
|
361
|
+
lengthOf(elist.unwrap(list)),
|
|
362
|
+
abi.encodePacked(elist.unwrap(list), bytes32(randCounter))
|
|
363
|
+
)
|
|
364
|
+
);
|
|
365
|
+
allowTransientInternal(elist.unwrap(result), msg.sender);
|
|
366
|
+
uint256 id = getNextEventId();
|
|
367
|
+
setDigest(abi.encodePacked(elist.unwrap(result), id));
|
|
368
|
+
emit EListShuffle(list, randCounter, result, id);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/// @notice Reverses the order of elements in an encrypted list.
|
|
372
|
+
/// @dev Returns a new list with elements in reverse order; original list is unchanged.
|
|
373
|
+
/// @param list The encrypted list to reverse.
|
|
374
|
+
/// @return result A new encrypted list with elements in reverse order.
|
|
375
|
+
function listReverse(elist list)
|
|
376
|
+
external
|
|
377
|
+
payable
|
|
378
|
+
payingElistFee(_elistBits(elist.unwrap(list)))
|
|
379
|
+
returns (elist result)
|
|
380
|
+
{
|
|
381
|
+
checkInput(elist.unwrap(list), typeToBitMask(ETypes.List));
|
|
382
|
+
|
|
383
|
+
result = elist.wrap(
|
|
384
|
+
createListResultHandle(
|
|
385
|
+
EOps.EListReverse,
|
|
386
|
+
listTypeOf(elist.unwrap(list)),
|
|
387
|
+
lengthOf(elist.unwrap(list)),
|
|
388
|
+
abi.encodePacked(elist.unwrap(list))
|
|
389
|
+
)
|
|
390
|
+
);
|
|
391
|
+
allowTransientInternal(elist.unwrap(result), msg.sender);
|
|
392
|
+
uint256 id = getNextEventId();
|
|
393
|
+
setDigest(abi.encodePacked(elist.unwrap(result), id));
|
|
394
|
+
emit EListReverse(list, result, id);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
}
|