@inco/lightning 0.11.0-testnet-3 → 1.0.0-devnet-2
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 +99 -0
- package/package.json +2 -1
- package/src/IncoLightning.sol +13 -0
- package/src/IncoVerifier.sol +13 -0
- package/src/Lib.demonet.sol +1 -1
- package/src/Lib.devnet.sol +1 -1
- package/src/Lib.sol +1 -1
- package/src/Types.sol +1 -5
- package/src/interfaces/automata-interfaces/IPCCSRouterExtended.sol +2 -0
- package/src/libs/incoLightning_demonet_v11_473256067.sol +1242 -0
- package/src/libs/incoLightning_devnet_v12_873394282.sol +1242 -0
- package/src/lightning-parts/AccessControl/AdvancedAccessControl.sol +29 -4
- package/src/lightning-parts/AccessControl/AdvancedAccessControl.types.sol +12 -0
- package/src/lightning-parts/AccessControl/BaseAccessControlList.sol +8 -0
- package/src/lightning-parts/AccessControl/test/TestAdvancedAccessControl.t.sol +36 -9
- package/src/lightning-parts/DecryptionAttester.sol +15 -3
- package/src/lightning-parts/primitives/EventCounter.sol +7 -3
- package/src/periphery/SessionVerifier.sol +16 -14
- package/src/test/IncoTest.sol +18 -1
- package/src/test/TestLib.t.sol +0 -13
- package/src/test/TestPause.t.sol +152 -0
- package/src/test/TestUpgrade.t.sol +1 -1
- package/src/test/TestVerifierPause.t.sol +211 -0
- package/src/version/IncoLightningConfig.sol +1 -1
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// SPDX-License-Identifier: No License
|
|
2
|
+
pragma solidity ^0.8;
|
|
3
|
+
|
|
4
|
+
import {inco} from "../Lib.sol";
|
|
5
|
+
import {euint256, ALLOWANCE_GRANTED_MAGIC_VALUE} from "../Types.sol";
|
|
6
|
+
import {IncoTest} from "./IncoTest.sol";
|
|
7
|
+
import {IIncoVerifier} from "../interfaces/IIncoVerifier.sol";
|
|
8
|
+
import {IncoVerifier} from "../IncoVerifier.sol";
|
|
9
|
+
import {AdvancedAccessControl} from "../lightning-parts/AccessControl/AdvancedAccessControl.sol";
|
|
10
|
+
import {AllowanceVoucher, AllowanceProof} from "../lightning-parts/AccessControl/AdvancedAccessControl.sol";
|
|
11
|
+
import {REQUIRED_ALLOWANCE_VOUCHER_WARNING} from "../lightning-parts/AccessControl/AdvancedAccessControl.types.sol";
|
|
12
|
+
import {DecryptionAttester} from "../lightning-parts/DecryptionAttester.sol";
|
|
13
|
+
import {
|
|
14
|
+
DecryptionAttestation,
|
|
15
|
+
ElementAttestationWithProof,
|
|
16
|
+
ReencryptionAttestation
|
|
17
|
+
} from "../lightning-parts/DecryptionAttester.types.sol";
|
|
18
|
+
|
|
19
|
+
/// @dev Approves any caller — simplest possible voucher verifier so the proof flow doesn't
|
|
20
|
+
/// shadow the property under test (which is the pause gate, not voucher logic).
|
|
21
|
+
contract AlwaysApprove {
|
|
22
|
+
|
|
23
|
+
function check(bytes32, address, bytes memory, bytes memory) public pure returns (bytes32) {
|
|
24
|
+
return ALLOWANCE_GRANTED_MAGIC_VALUE;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// @dev Minimal test contract — reverts if the verifier reports the attestation invalid.
|
|
30
|
+
contract TestContract {
|
|
31
|
+
|
|
32
|
+
error InvalidAttestation();
|
|
33
|
+
|
|
34
|
+
IIncoVerifier verifier;
|
|
35
|
+
|
|
36
|
+
constructor(IIncoVerifier _verifier) {
|
|
37
|
+
verifier = _verifier;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function verify(DecryptionAttestation memory att, bytes[] calldata signatures) external view {
|
|
41
|
+
if (!verifier.isValidDecryptionAttestation(att, signatures)) {
|
|
42
|
+
revert InvalidAttestation();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
contract TestVerifierPause is IncoTest {
|
|
49
|
+
|
|
50
|
+
function testValidAttestationsBecomeInvalidWhenVerifierIsPaused() public {
|
|
51
|
+
IIncoVerifier verifier = inco.incoVerifier();
|
|
52
|
+
|
|
53
|
+
// Set up a valid decryption attestation for a handle alice owns
|
|
54
|
+
vm.startPrank(alice);
|
|
55
|
+
euint256 secret = inco.asEuint256(42);
|
|
56
|
+
inco.allow(euint256.unwrap(secret), alice);
|
|
57
|
+
vm.stopPrank();
|
|
58
|
+
bytes32 handle = euint256.unwrap(secret);
|
|
59
|
+
|
|
60
|
+
AllowanceProof memory emptyProof; // sharer == address(0) → no voucher needed
|
|
61
|
+
(DecryptionAttestation memory att, bytes[] memory sigs) =
|
|
62
|
+
getDecryptionAttestation(alice, HandleWithProof({handle: handle, proof: emptyProof}));
|
|
63
|
+
|
|
64
|
+
// Set up a valid voucher-based AllowanceProof so isAllowedWithProof returns true
|
|
65
|
+
AlwaysApprove approver = new AlwaysApprove();
|
|
66
|
+
AllowanceVoucher memory voucher = AllowanceVoucher({
|
|
67
|
+
sessionNonce: bytes32(0),
|
|
68
|
+
verifyingContract: address(approver),
|
|
69
|
+
callFunction: approver.check.selector,
|
|
70
|
+
sharerArgData: "",
|
|
71
|
+
warning: REQUIRED_ALLOWANCE_VOUCHER_WARNING
|
|
72
|
+
});
|
|
73
|
+
AllowanceProof memory proof = AllowanceProof({
|
|
74
|
+
sharer: alice,
|
|
75
|
+
voucher: voucher,
|
|
76
|
+
voucherSignature: getSignatureForDigest(verifier.allowanceVoucherDigest(voucher), alicePrivKey),
|
|
77
|
+
requesterArgData: ""
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Set up valid reencryption + elist attestations (helpers scope away locals
|
|
81
|
+
// that would otherwise blow the 16-slot stack limit in this function)
|
|
82
|
+
(ReencryptionAttestation[] memory reAtts, bytes[] memory reSigs) = _buildValidReencryption(verifier, handle);
|
|
83
|
+
(ElementAttestationWithProof[] memory proofElements, bytes32 elistProof, bytes[] memory elistSigs) =
|
|
84
|
+
_buildValidElistAttestation(verifier, handle);
|
|
85
|
+
|
|
86
|
+
TestContract testContract = new TestContract(verifier);
|
|
87
|
+
|
|
88
|
+
// Sanity: every attestation is valid before pause
|
|
89
|
+
assertTrue(verifier.isValidDecryptionAttestation(att, sigs), "decryption attestation should be valid");
|
|
90
|
+
assertTrue(verifier.isAllowedWithProof(handle, bob, proof), "voucher proof should grant access");
|
|
91
|
+
assertTrue(verifier.isValidReencryptionAttestation(reAtts, reSigs), "reencryption attestation should be valid");
|
|
92
|
+
assertTrue(
|
|
93
|
+
verifier.isValidEListDecryptionAttestation(handle, proofElements, elistProof, elistSigs),
|
|
94
|
+
"elist decryption attestation should be valid"
|
|
95
|
+
);
|
|
96
|
+
testContract.verify(att, sigs); // shouldn't revert
|
|
97
|
+
|
|
98
|
+
// Pause the verifier
|
|
99
|
+
vm.prank(owner);
|
|
100
|
+
IncoVerifier(address(verifier)).pause();
|
|
101
|
+
|
|
102
|
+
// Pause flips every verifier check to false for the same inputs
|
|
103
|
+
assertFalse(
|
|
104
|
+
verifier.isValidDecryptionAttestation(att, sigs), "decryption attestation should be invalid when paused"
|
|
105
|
+
);
|
|
106
|
+
assertFalse(verifier.isAllowedWithProof(handle, bob, proof), "voucher proof should be rejected when paused");
|
|
107
|
+
assertFalse(
|
|
108
|
+
verifier.isValidReencryptionAttestation(reAtts, reSigs),
|
|
109
|
+
"reencryption attestation should be invalid when paused"
|
|
110
|
+
);
|
|
111
|
+
assertFalse(
|
|
112
|
+
verifier.isValidEListDecryptionAttestation(handle, proofElements, elistProof, elistSigs),
|
|
113
|
+
"elist decryption attestation should be invalid when paused"
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Downstream test contract that trusts the verifier reverts
|
|
117
|
+
vm.expectRevert(TestContract.InvalidAttestation.selector);
|
|
118
|
+
testContract.verify(att, sigs);
|
|
119
|
+
|
|
120
|
+
// Unpause restores validity (the attestation/proof bytes are unchanged)
|
|
121
|
+
vm.prank(owner);
|
|
122
|
+
IncoVerifier(address(verifier)).unpause();
|
|
123
|
+
assertTrue(verifier.isValidDecryptionAttestation(att, sigs), "decryption attestation should be valid again");
|
|
124
|
+
assertTrue(verifier.isAllowedWithProof(handle, bob, proof), "voucher proof should grant access again");
|
|
125
|
+
assertTrue(
|
|
126
|
+
verifier.isValidReencryptionAttestation(reAtts, reSigs), "reencryption attestation should be valid again"
|
|
127
|
+
);
|
|
128
|
+
assertTrue(
|
|
129
|
+
verifier.isValidEListDecryptionAttestation(handle, proofElements, elistProof, elistSigs),
|
|
130
|
+
"elist decryption attestation should be valid again"
|
|
131
|
+
);
|
|
132
|
+
testContract.verify(att, sigs); // shouldn't revert
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// @dev helper to build a valid ReencryptionAttestation + signatures that pass the verifier checks when not paused.
|
|
136
|
+
function _buildValidReencryption(IIncoVerifier verifier, bytes32 handle)
|
|
137
|
+
private
|
|
138
|
+
view
|
|
139
|
+
returns (ReencryptionAttestation[] memory atts, bytes[] memory sigs)
|
|
140
|
+
{
|
|
141
|
+
atts = new ReencryptionAttestation[](1);
|
|
142
|
+
atts[0] =
|
|
143
|
+
ReencryptionAttestation({handle: handle, userCiphertext: hex"deadbeef", encryptedSignature: hex"cafebabe"});
|
|
144
|
+
sigs = new bytes[](1);
|
|
145
|
+
sigs[0] = getSignatureForDigest(verifier.reencryptionAttestationDigest(atts[0]), teePrivKey);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// @dev helper to build a valid EListAttestation + signatures that pass the verifier checks when not paused.
|
|
149
|
+
function _buildValidElistAttestation(IIncoVerifier verifier, bytes32 handle)
|
|
150
|
+
private
|
|
151
|
+
view
|
|
152
|
+
returns (ElementAttestationWithProof[] memory elements, bytes32 proof, bytes[] memory sigs)
|
|
153
|
+
{
|
|
154
|
+
elements = new ElementAttestationWithProof[](1);
|
|
155
|
+
elements[0] =
|
|
156
|
+
ElementAttestationWithProof({pairHash: keccak256("elem"), commitment: bytes32(0), value: bytes32(0)});
|
|
157
|
+
bytes32[] memory hashes = new bytes32[](1);
|
|
158
|
+
hashes[0] = elements[0].pairHash;
|
|
159
|
+
proof = keccak256(abi.encodePacked(hashes));
|
|
160
|
+
sigs = new bytes[](1);
|
|
161
|
+
sigs[0] = getSignatureForDigest(
|
|
162
|
+
verifier.decryptionAttestationDigest(DecryptionAttestation({handle: handle, value: proof})), teePrivKey
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// @dev Security-critical: when paused, the four verifier checks must short-circuit to
|
|
167
|
+
/// false BEFORE running any input validation.
|
|
168
|
+
function testPausedVerifierShortCircuitsBeforeInputValidation() public {
|
|
169
|
+
IIncoVerifier verifier = inco.incoVerifier();
|
|
170
|
+
|
|
171
|
+
// Inputs designed to exercise the unpaused revert paths in two of the four checks.
|
|
172
|
+
// (`isValidDecryptionAttestation` and `isValidEListDecryptionAttestation` use
|
|
173
|
+
// tryRecover + length-based early returns, so they don't have a clean malformed-revert
|
|
174
|
+
// path — for them we just assert the pause gate cleanly returns false.)
|
|
175
|
+
AllowanceProof memory garbageProof; // voucher.warning == "" → InvalidVoucherWarning
|
|
176
|
+
ReencryptionAttestation[] memory atts = new ReencryptionAttestation[](2);
|
|
177
|
+
bytes[] memory sigsLenOne = new bytes[](1); // mismatched → AttestationsSignaturesLengthMismatch
|
|
178
|
+
bytes[] memory sigsEmpty = new bytes[](0);
|
|
179
|
+
DecryptionAttestation memory garbageAtt;
|
|
180
|
+
ElementAttestationWithProof[] memory emptyElements = new ElementAttestationWithProof[](0);
|
|
181
|
+
|
|
182
|
+
// Sanity: the malformed inputs DO revert when not paused
|
|
183
|
+
vm.expectRevert(AdvancedAccessControl.InvalidVoucherWarning.selector);
|
|
184
|
+
verifier.isAllowedWithProof(bytes32(0), address(0), garbageProof);
|
|
185
|
+
|
|
186
|
+
vm.expectRevert(abi.encodeWithSelector(DecryptionAttester.AttestationsSignaturesLengthMismatch.selector, 2, 1));
|
|
187
|
+
verifier.isValidReencryptionAttestation(atts, sigsLenOne);
|
|
188
|
+
|
|
189
|
+
// Pause; the same calls must return false WITHOUT reverting
|
|
190
|
+
vm.prank(owner);
|
|
191
|
+
IncoVerifier(address(verifier)).pause();
|
|
192
|
+
|
|
193
|
+
assertFalse(
|
|
194
|
+
verifier.isAllowedWithProof(bytes32(0), address(0), garbageProof),
|
|
195
|
+
"isAllowedWithProof must return false (not revert) when paused with garbage proof"
|
|
196
|
+
);
|
|
197
|
+
assertFalse(
|
|
198
|
+
verifier.isValidReencryptionAttestation(atts, sigsLenOne),
|
|
199
|
+
"isValidReencryptionAttestation must return false (not revert) when paused with mismatched arrays"
|
|
200
|
+
);
|
|
201
|
+
assertFalse(
|
|
202
|
+
verifier.isValidDecryptionAttestation(garbageAtt, sigsEmpty),
|
|
203
|
+
"isValidDecryptionAttestation must return false when paused"
|
|
204
|
+
);
|
|
205
|
+
assertFalse(
|
|
206
|
+
verifier.isValidEListDecryptionAttestation(bytes32(0), emptyElements, bytes32(0), sigsEmpty),
|
|
207
|
+
"isValidEListDecryptionAttestation must return false when paused"
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
}
|
|
@@ -7,7 +7,7 @@ pragma solidity ^0.8;
|
|
|
7
7
|
// UPDATE the CHANGELOG on new versions
|
|
8
8
|
|
|
9
9
|
string constant CONTRACT_NAME = "incoLightning";
|
|
10
|
-
uint8 constant MAJOR_VERSION =
|
|
10
|
+
uint8 constant MAJOR_VERSION = 12;
|
|
11
11
|
uint8 constant MINOR_VERSION = 0;
|
|
12
12
|
// whenever a new version is deployed, we need to pump this up
|
|
13
13
|
// otherwise make test_upgrade will fail
|