@inco/lightning 0.8.0-devnet-20 → 0.8.0-devnet-23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inco/lightning",
3
- "version": "0.8.0-devnet-20",
3
+ "version": "0.8.0-devnet-23",
4
4
  "repository": "https://github.com/Inco-fhevm/inco-monorepo",
5
5
  "files": [
6
6
  "src/",
@@ -1,8 +1,13 @@
1
1
  // SPDX-License-Identifier: No License
2
2
  pragma solidity ^0.8;
3
3
 
4
- import {DecryptionAttestation, ElementAttestationWithProof} from "./DecryptionAttester.types.sol";
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,6 +43,22 @@ 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.
@@ -77,4 +108,59 @@ abstract contract DecryptionAttester is IDecryptionAttester, SignatureVerifier,
77
108
  return isValidDecryptionAttestation(attestation, signatures);
78
109
  }
79
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
+
80
166
  }
@@ -19,3 +19,9 @@ struct ElementAttestationWithProof {
19
19
  bytes32 commitment;
20
20
  bytes32 value;
21
21
  }
22
+
23
+ struct ReencryptionAttestation {
24
+ bytes32 handle;
25
+ bytes userCiphertext;
26
+ bytes encryptedSignature;
27
+ }
@@ -1,7 +1,11 @@
1
1
  // SPDX-License-Identifier: No License
2
2
  pragma solidity ^0.8;
3
3
 
4
- import {DecryptionAttestation, ElementAttestationWithProof} from "../DecryptionAttester.types.sol";
4
+ import {
5
+ DecryptionAttestation,
6
+ ElementAttestationWithProof,
7
+ ReencryptionAttestation
8
+ } from "../DecryptionAttester.types.sol";
5
9
 
6
10
  interface IDecryptionAttester {
7
11
 
@@ -16,5 +20,10 @@ interface IDecryptionAttester {
16
20
  bytes32 proof,
17
21
  bytes[] memory signatures
18
22
  ) external view returns (bool);
23
+ function reencryptionAttestationDigest(ReencryptionAttestation memory attestation) external view returns (bytes32);
24
+ function isValidReencryptionAttestation(
25
+ ReencryptionAttestation[] calldata attestations,
26
+ bytes[] calldata signatures
27
+ ) external view returns (bool);
19
28
 
20
29
  }