@inco/lightning 0.2.17 → 0.3.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.
Files changed (42) hide show
  1. package/manifest.yaml +29 -0
  2. package/package.json +5 -1
  3. package/src/DeployTEE.sol +153 -0
  4. package/src/DeployUtils.sol +17 -9
  5. package/src/Errors.sol +4 -0
  6. package/src/IncoLightning.gen.sol +1 -1
  7. package/src/IncoLightning.sol +2 -1
  8. package/src/Types.sol +45 -3
  9. package/src/lightning-parts/EncryptedInput.gen.sol +1 -0
  10. package/src/lightning-parts/EncryptedInput.sol +1 -1
  11. package/src/lightning-parts/EncryptedOperations.gen.sol +1 -1
  12. package/src/lightning-parts/EncryptedOperations.sol +8 -6
  13. package/src/lightning-parts/TEELifecycle.gen.sol +58 -0
  14. package/src/lightning-parts/TEELifecycle.sol +255 -0
  15. package/src/lightning-parts/TEELifecycle.types.sol +21 -0
  16. package/src/lightning-parts/primitives/SignatureVerifier.gen.sol +1 -0
  17. package/src/lightning-parts/primitives/SignatureVerifier.sol +11 -0
  18. package/src/lightning-parts/test/HandleMetadata.t.sol +1 -1
  19. package/src/test/FakeIncoInfra/FakeQuoteVerifier.sol +29 -0
  20. package/src/test/FakeIncoInfra/MockRemoteAttestation.sol +37 -0
  21. package/src/test/FibonacciDecrypt.sol +1 -0
  22. package/src/test/IncoTest.sol +5 -1
  23. package/src/test/TEELifecycle/README.md +53 -0
  24. package/src/test/TEELifecycle/TEELifecycleHWTest.t.sol +119 -0
  25. package/src/test/TEELifecycle/TEELifecycleMockTest.t.sol +145 -0
  26. package/src/test/TEELifecycle/addnode_data/eoa.txt +1 -0
  27. package/src/test/TEELifecycle/addnode_data/quote.bin +0 -0
  28. package/src/test/TEELifecycle/bootstrap_data/ecies_pubkey.bin +1 -0
  29. package/src/test/TEELifecycle/bootstrap_data/eip712_signature.bin +1 -0
  30. package/src/test/TEELifecycle/bootstrap_data/eoa.txt +1 -0
  31. package/src/test/TEELifecycle/bootstrap_data/qe_identity +1 -0
  32. package/src/test/TEELifecycle/bootstrap_data/qe_identity_signature.bin +1 -0
  33. package/src/test/TEELifecycle/bootstrap_data/quote.bin +0 -0
  34. package/src/test/TEELifecycle/bootstrap_data/tcb_info +1 -0
  35. package/src/test/TEELifecycle/bootstrap_data/tcb_info_signature.bin +1 -0
  36. package/src/test/TEELifecycle/test_cert/AttestationReportSigningCA.crl +0 -0
  37. package/src/test/TEELifecycle/test_cert/Intel_SGX_Attestation_RootCA.cer +0 -0
  38. package/src/test/TEELifecycle/test_cert/Intel_SGX_PCK_CRL.crl +0 -0
  39. package/src/test/TEELifecycle/test_cert/Intel_SGX_PCK_PlatformCA.cer +0 -0
  40. package/src/test/TEELifecycle/test_cert/Intel_SGX_TCB_Signing.cer +0 -0
  41. package/src/test/TestFakeInfra.t.sol +18 -1
  42. package/src/test/TestUpgrade.t.sol +314 -0
@@ -0,0 +1,145 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity ^0.8.0;
3
+
4
+ import "@inco/lightning/src/lightning-parts/TEELifecycle.sol";
5
+ import "@inco/lightning/src/lightning-parts/TEELifecycle.types.sol";
6
+ import {MockRemoteAttestation} from "../FakeIncoInfra/MockRemoteAttestation.sol";
7
+ import {FakeQuoteVerifier} from "../FakeIncoInfra/FakeQuoteVerifier.sol";
8
+
9
+ import "forge-std/Vm.sol";
10
+ import "forge-std/Test.sol";
11
+
12
+ contract TEELifecycleMockTest is Test, MockRemoteAttestation, TEELifecycle {
13
+
14
+ function setUp() public {
15
+ quoteVerifier = new FakeQuoteVerifier();
16
+ }
17
+
18
+ function test_successfulBootstrap() public {
19
+ (BootstrapResult memory bootstrapResult, , , bytes memory quote, bytes memory signature, bytes memory mrtd) = successfulBootstrapResult();
20
+ vm.startPrank(this.owner());
21
+ this.approveNewTEEVersion(mrtd);
22
+ this.verifyBootstrapResult(
23
+ bootstrapResult,
24
+ quote,
25
+ signature
26
+ );
27
+ assertTrue(this.isBootstrapComplete(), "Bootstrap should be complete");
28
+ vm.stopPrank();
29
+ }
30
+
31
+ function test_invalidMrtd() public {
32
+ bytes memory badMrtd = hex"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
33
+
34
+ (BootstrapResult memory bootstrapResult, , address bootstrapPartyAddress, bytes memory quote, bytes memory signature, bytes memory mrtd) = successfulBootstrapResult();
35
+
36
+ quote = createQuote(badMrtd, bootstrapPartyAddress); // Replace with bad MRTD
37
+ vm.startPrank(this.owner());
38
+ this.approveNewTEEVersion(mrtd);
39
+ vm.expectRevert(bytes("Invalid report MRTD"));
40
+ this.verifyBootstrapResult(
41
+ bootstrapResult,
42
+ quote,
43
+ signature
44
+ );
45
+ vm.stopPrank();
46
+ }
47
+
48
+ function test_invalidSignature() public {
49
+ (BootstrapResult memory bootstrapResult, , , bytes memory quote, , bytes memory mrtd) = successfulBootstrapResult();
50
+ (uint256 bootstrapPartyFakePrivkey, ) = getLabeledKeyPair("bootstrapPartyFake");
51
+ bytes memory signatureInvalid = signBootstrapResult(bootstrapResult, bootstrapPartyFakePrivkey);
52
+ vm.startPrank(this.owner());
53
+ this.approveNewTEEVersion(mrtd);
54
+ vm.expectRevert(bytes("Invalid signature for bootstrap data"));
55
+ this.verifyBootstrapResult(
56
+ bootstrapResult,
57
+ quote,
58
+ signatureInvalid
59
+ );
60
+ vm.stopPrank();
61
+ }
62
+
63
+ function test_bootstrapAlreadyComplete() public {
64
+ (BootstrapResult memory bootstrapResult, , , bytes memory quote, bytes memory signature, bytes memory mrtd) = successfulBootstrapResult();
65
+ vm.startPrank(this.owner());
66
+ this.approveNewTEEVersion(mrtd);
67
+ this.verifyBootstrapResult(
68
+ bootstrapResult,
69
+ quote,
70
+ signature
71
+ );
72
+ vm.expectRevert(bytes("Bootstrap already completed"));
73
+ this.verifyBootstrapResult(
74
+ bootstrapResult,
75
+ quote,
76
+ signature
77
+ );
78
+ vm.stopPrank();
79
+ }
80
+
81
+ function test_approveNewTEEInvalidMrtd() public {
82
+ bytes memory mrtd = hex"deadbeef";
83
+ vm.startPrank(this.owner());
84
+ vm.expectRevert(bytes("MRTD must be 48 bytes"));
85
+ this.approveNewTEEVersion(mrtd);
86
+ vm.stopPrank();
87
+ }
88
+
89
+ function test_bootstrapNotCompleteNewCoval() public {
90
+ bytes memory mrtd = hex"2a90c8fa38672cafd791d994beb6836b99383b2563736858632284f0f760a6446efd1e7ec457cf08b629ea630f7b4525";
91
+ (, address newCoval) = getLabeledKeyPair("newCoval");
92
+ bytes memory quote = createQuote(mrtd, newCoval);
93
+ vm.startPrank(this.owner());
94
+ vm.expectRevert(bytes("Bootstrap not complete"));
95
+ this.addNewCovalidator(quote);
96
+ vm.stopPrank();
97
+ }
98
+
99
+ function test_invalidMrtdNewCoval() public {
100
+ (BootstrapResult memory bootstrapResult, , , bytes memory quote, bytes memory signature, bytes memory mrtd) = successfulBootstrapResult();
101
+ vm.startPrank(this.owner());
102
+ this.approveNewTEEVersion(mrtd);
103
+ this.verifyBootstrapResult(
104
+ bootstrapResult,
105
+ quote,
106
+ signature
107
+ );
108
+ bytes memory badMrtd = hex"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
109
+ (, address newCoval) = getLabeledKeyPair("newCoval");
110
+ bytes memory quoteNew = createQuote(badMrtd, newCoval);
111
+
112
+ vm.expectRevert(bytes("Invalid report MRTD"));
113
+ this.addNewCovalidator(quoteNew);
114
+ vm.stopPrank();
115
+ }
116
+
117
+ // Helper function to create a successful bootstrap result
118
+ function successfulBootstrapResult() internal returns (BootstrapResult memory bootstrapResult, uint256 bootstrapPartyPrivkey, address bootstrapPartyAddress, bytes memory quote, bytes memory signature, bytes memory mrtd) {
119
+ (bootstrapPartyPrivkey, bootstrapPartyAddress) = getLabeledKeyPair("bootstrapParty");
120
+ bytes memory eciesPubkey = hex"04ff5c6dd72ad7583288b84ee2598e081fe0bc6ef543c342e925a5dfcff9afb2444d25454d7d5dcfadc9ed99477c245efa93caf58d7f58143300d81cc948e7bdf5";
121
+ mrtd = hex"2a90c8fa38672cafd791d994beb6836b99383b2563736858632284f0f760a6446efd1e7ec457cf08b629ea630f7b4525";
122
+
123
+ bootstrapResult = BootstrapResult({
124
+ ecies_pubkey: eciesPubkey
125
+ });
126
+
127
+ quote = createQuote(
128
+ mrtd,
129
+ bootstrapPartyAddress
130
+ );
131
+ signature = signBootstrapResult(bootstrapResult, bootstrapPartyPrivkey);
132
+ }
133
+
134
+ // Helper function to sign the bootstrap result
135
+ function signBootstrapResult(
136
+ BootstrapResult memory bootstrapResult,
137
+ uint256 privateKey
138
+ ) internal view returns (bytes memory) {
139
+ bytes32 bootstrapResultDigest = bootstrapResultDigest(
140
+ bootstrapResult
141
+ );
142
+ return getSignatureForDigest(bootstrapResultDigest, privateKey);
143
+ }
144
+
145
+ }
@@ -0,0 +1 @@
1
+ 0x32B70C13186E3B81137ec09c0CB3ee8e14e69f64
@@ -0,0 +1 @@
1
+ �@��3��X���r�F��[s�X"�t��$��
@@ -0,0 +1 @@
1
+ �a�[O$��������G�s#������@ZH �W#��3�-`�x'r�U�?G�)|H3}�K;
@@ -0,0 +1 @@
1
+ 0x8461984E47D19e9B86702F398982a5c2747A734D
@@ -0,0 +1 @@
1
+ {"id":"TD_QE","version":2,"issueDate":"2025-08-06T16:32:25Z","nextUpdate":"2025-09-05T16:32:25Z","tcbEvaluationDataNumber":17,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"DC9E2A7C6F948F17474E34A7FC43ED030F7C1563F1BABDDF6340C82E0E54A8C5","isvprodid":2,"tcbLevels":[{"tcb":{"isvsvn":4},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"}]}
@@ -0,0 +1 @@
1
+ ETw���bwޱ�˧����-ĭ�%u#O�Po�����'�ࣳE&T�d�.F�����|��
@@ -0,0 +1 @@
1
+ {"id":"TDX","version":3,"issueDate":"2025-08-06T16:25:19Z","nextUpdate":"2025-09-05T16:25:19Z","fmspc":"00806F050000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":17,"tdxModule":{"mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF"},"tdxModuleIdentities":[{"id":"TDX_03","mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF","tcbLevels":[{"tcb":{"isvsvn":3},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"}]},{"id":"TDX_01","mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF","tcbLevels":[{"tcb":{"isvsvn":4},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":2},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"OutOfDate"}]}],"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":7,"category":"BIOS","type":"Early Microcode Update"},{"svn":7,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11,"tdxtcbcomponents":[{"svn":5,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":7,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":6,"category":"BIOS","type":"Early Microcode Update"},{"svn":6,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":6,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00960","INTEL-SA-00982","INTEL-SA-00986"]},{"tcb":{"sgxtcbcomponents":[{"svn":5,"category":"BIOS","type":"Early Microcode Update"},{"svn":5,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":5,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00837","INTEL-SA-00960","INTEL-SA-00982","INTEL-SA-00986"]},{"tcb":{"sgxtcbcomponents":[{"svn":5,"category":"BIOS","type":"Early Microcode Update"},{"svn":5,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":5,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00233","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00320","INTEL-SA-00329","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00477","INTEL-SA-00837","INTEL-SA-00960","INTEL-SA-00982","INTEL-SA-00986"]}]}
@@ -0,0 +1 @@
1
+ ��e�3?��b� \���@E\�B���2���v3��KLm�C����
@@ -4,6 +4,11 @@ pragma solidity ^0.8;
4
4
  import {IncoTest} from "./IncoTest.sol";
5
5
  import {e, euint256, ebool, eaddress} from "../Lib.sol";
6
6
  import {SenderNotAllowedForHandle} from "../Types.sol";
7
+ import {TEELifecycle} from "../lightning-parts/TEELifecycle.sol";
8
+ import {MockRemoteAttestation} from "./FakeIncoInfra/MockRemoteAttestation.sol";
9
+
10
+ import {MINIMUM_QUOTE_LENGTH} from "@automata-network/dcap-attestation/types/Constants.sol";
11
+ import {TD10ReportBody} from "@automata-network/dcap-attestation/types/V4Structs.sol";
7
12
 
8
13
  contract TakesEInput {
9
14
  using e for bytes;
@@ -36,7 +41,7 @@ contract TakesEInput {
36
41
  }
37
42
 
38
43
  // its meta: this is testing correct behavior of our testing infrastructure
39
- contract TestFakeInfra is IncoTest {
44
+ contract TestFakeInfra is IncoTest, MockRemoteAttestation {
40
45
  using e for euint256;
41
46
  using e for ebool;
42
47
  using e for uint256;
@@ -303,4 +308,16 @@ contract TestFakeInfra is IncoTest {
303
308
  );
304
309
  a.add(euint256.wrap(randomHandle));
305
310
  }
311
+
312
+ function testCreateQuote() public {
313
+ bytes memory mrtd = hex"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
314
+ address signer = address(0x1234567890123456789012345678901234567890);
315
+ bytes memory quote = createQuote(mrtd, signer);
316
+ TEELifecycle lifecycle = new TEELifecycle();
317
+ TD10ReportBody memory tdReport = lifecycle.parseTD10ReportBody(quote);
318
+ (address reportDataSigner, bytes memory reportMrtd) = lifecycle.parseReport(tdReport);
319
+ assertEq(reportDataSigner, signer);
320
+ assertEq(keccak256(reportMrtd), keccak256(mrtd));
321
+ assertEq(quote.length, MINIMUM_QUOTE_LENGTH);
322
+ }
306
323
  }
@@ -0,0 +1,314 @@
1
+ // SPDX-License-Identifier: No License
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
5
+ import {Safe} from "safe-smart-account/Safe.sol";
6
+ import {SafeProxyFactory} from "safe-smart-account/proxies/SafeProxyFactory.sol";
7
+ import {Enum} from "safe-smart-account/libraries/Enum.sol";
8
+ import {IOwnerManager} from "safe-smart-account/interfaces/IOwnerManager.sol";
9
+ import {IncoTest} from "./IncoTest.sol";
10
+ import {inco} from "../Lib.sol";
11
+ import {IncoLightning} from "../IncoLightning.sol";
12
+ import {
13
+ MAJOR_VERSION,
14
+ MINOR_VERSION,
15
+ PATCH_VERSION
16
+ } from "../version/IncoLightningConfig.sol";
17
+
18
+ interface IUUPS {
19
+ function upgradeToAndCall(
20
+ address newImplementation,
21
+ bytes calldata data
22
+ ) external payable;
23
+ }
24
+
25
+ contract IncoLightningV2 is IncoLightning {
26
+ uint8 constant MAJOR_VERSION_MOCK = 255;
27
+ uint8 constant MINOR_VERSION_MOCK = 255;
28
+ uint8 constant PATCH_VERSION_MOCK = 255;
29
+
30
+ constructor(bytes32 salt) IncoLightning(salt) {}
31
+
32
+ function getVersion() public view virtual override returns (string memory) {
33
+ return
34
+ versionString(
35
+ MAJOR_VERSION_MOCK,
36
+ MINOR_VERSION_MOCK,
37
+ PATCH_VERSION_MOCK
38
+ );
39
+ }
40
+ }
41
+
42
+ contract TestUpgrade is IncoTest {
43
+ using Strings for uint256;
44
+
45
+ // EIP-1967 implementation slot
46
+ bytes32 private constant IMPLEMENTATION_SLOT =
47
+ 0x360894A13BA1A3210667C828492DB98DCA3E2076CC3735A920A3CA505D382BBC;
48
+
49
+ address private incoProxyAddr;
50
+ IncoLightning private v1Impl;
51
+ IncoLightningV2 private v2Impl;
52
+
53
+ Safe private safe;
54
+
55
+ function setUp() public override {
56
+ super.setUp();
57
+ incoProxyAddr = address(inco);
58
+
59
+ Safe master = new Safe();
60
+ SafeProxyFactory factory = new SafeProxyFactory();
61
+
62
+ address[] memory owners = new address[](3);
63
+ owners[0] = alice;
64
+ owners[1] = bob;
65
+ owners[2] = carol;
66
+ uint256 threshold = 2;
67
+ bytes memory setupData = abi.encodeWithSelector(
68
+ Safe.setup.selector,
69
+ owners,
70
+ threshold,
71
+ address(0),
72
+ bytes(""),
73
+ address(0),
74
+ address(0),
75
+ 0,
76
+ payable(address(0))
77
+ );
78
+ safe = Safe(
79
+ payable(factory.createProxyWithNonce(address(master), setupData, 0))
80
+ );
81
+
82
+ vm.prank(owner);
83
+ inco.transferOwnership(address(safe));
84
+
85
+ // Deploy V2
86
+ bytes32 salt = getSalt(
87
+ "IncoLightningV2",
88
+ 255,
89
+ 255,
90
+ 255,
91
+ testDeployer,
92
+ "testnet"
93
+ );
94
+ v2Impl = new IncoLightningV2(salt);
95
+ }
96
+
97
+ function test_SafeUpgrade2of3_Succeeds() public {
98
+ string memory versionBefore = inco.getVersion();
99
+ assertEq(
100
+ versionBefore,
101
+ string.concat(
102
+ uint256(MAJOR_VERSION).toString(),
103
+ "_",
104
+ uint256(MINOR_VERSION).toString(),
105
+ "_",
106
+ uint256(PATCH_VERSION).toString()
107
+ )
108
+ );
109
+ // data to sign
110
+ bytes memory data = abi.encodeWithSelector(
111
+ IUUPS.upgradeToAndCall.selector,
112
+ address(v2Impl),
113
+ ""
114
+ );
115
+ // prepare txhash
116
+ bytes32 txHash = _txHash(safe, incoProxyAddr, 0, data);
117
+ // sign txHash
118
+ bytes memory sigA = _sign(alicePrivKey, txHash);
119
+ bytes memory sigB = _sign(bobPrivKey, txHash);
120
+ // sort signatures (asc)
121
+ bytes memory signatures = _packSortedTwo(sigA, alice, sigB, bob);
122
+ // execute tx with sorted signatures
123
+ _execSafe(safe, incoProxyAddr, 0, data, signatures);
124
+
125
+ address implAfter = address(
126
+ uint160(uint256(vm.load(incoProxyAddr, IMPLEMENTATION_SLOT)))
127
+ );
128
+ assertEq(implAfter, address(v2Impl));
129
+
130
+ string memory versionAfter = inco.getVersion();
131
+ assertEq(versionAfter, "255_255_255");
132
+ }
133
+
134
+ function test_SafeSingleSignature_Fails() public {
135
+ bytes memory data = abi.encodeWithSelector(
136
+ IUUPS.upgradeToAndCall.selector,
137
+ address(v2Impl),
138
+ ""
139
+ );
140
+ bytes32 txHash = _txHash(safe, incoProxyAddr, 0, data);
141
+ bytes memory sigA = _sign(alicePrivKey, txHash);
142
+ vm.expectRevert();
143
+ _execSafe(safe, incoProxyAddr, 0, data, sigA);
144
+ }
145
+
146
+ function test_Safe_UnsortedSignatures_Fails() public {
147
+ bytes memory data = abi.encodeWithSelector(
148
+ IUUPS.upgradeToAndCall.selector,
149
+ address(v2Impl),
150
+ ""
151
+ );
152
+ bytes32 txHash = _txHash(safe, incoProxyAddr, 0, data);
153
+ bytes memory sigA = _sign(alicePrivKey, txHash);
154
+ bytes memory sigC = _sign(carolPrivKey, txHash);
155
+ bytes memory signatures = bytes.concat(sigC, sigA); // unsorted
156
+ vm.expectRevert();
157
+ _execSafe(safe, incoProxyAddr, 0, data, signatures);
158
+ }
159
+
160
+ function test_Safe_WrongSigner_Fails() public {
161
+ bytes memory data = abi.encodeWithSelector(
162
+ IUUPS.upgradeToAndCall.selector,
163
+ address(v2Impl),
164
+ ""
165
+ );
166
+ bytes32 txHash = _txHash(safe, incoProxyAddr, 0, data);
167
+ bytes memory sigOwner = _sign(alicePrivKey, txHash);
168
+ bytes memory sigAttacker = _sign(davePrivKey, txHash);
169
+ bytes memory signatures = _packSortedTwo(
170
+ sigOwner,
171
+ alice,
172
+ sigAttacker,
173
+ dave
174
+ ); // dave not an owner
175
+ vm.expectRevert();
176
+ _execSafe(safe, incoProxyAddr, 0, data, signatures);
177
+ }
178
+
179
+ function test_Safe_ReplayNonce_Fails() public {
180
+ bytes memory data = abi.encodeWithSelector(
181
+ IUUPS.upgradeToAndCall.selector,
182
+ address(v2Impl),
183
+ ""
184
+ );
185
+ bytes32 txHash = _txHash(safe, incoProxyAddr, 0, data);
186
+ bytes memory sigA = _sign(alicePrivKey, txHash);
187
+ bytes memory sigB = _sign(bobPrivKey, txHash);
188
+ bytes memory signatures = _packSortedTwo(sigA, alice, sigB, bob);
189
+ _execSafe(safe, incoProxyAddr, 0, data, signatures);
190
+
191
+ string memory versionAfter = inco.getVersion();
192
+ assertEq(versionAfter, "255_255_255");
193
+
194
+ vm.expectRevert();
195
+ _execSafe(safe, incoProxyAddr, 0, data, signatures); // nonce advanced
196
+ }
197
+
198
+ function test_Safe_UpdateSigners_SwapOwner_ThenUpgrade() public {
199
+ // swap bob -> eve (prevOwner = alice since owners were [alice, bob, carol])
200
+ bytes memory change = abi.encodeWithSelector(
201
+ IOwnerManager.swapOwner.selector,
202
+ alice,
203
+ bob,
204
+ eve
205
+ );
206
+ bytes32 changeHash = _txHash(safe, address(safe), 0, change);
207
+ bytes memory changeSigA = _sign(alicePrivKey, changeHash);
208
+ bytes memory changeSigC = _sign(carolPrivKey, changeHash);
209
+ bytes memory changeSigs = _packSortedTwo(
210
+ changeSigA,
211
+ alice,
212
+ changeSigC,
213
+ carol
214
+ );
215
+ _execSafe(safe, address(safe), 0, change, changeSigs);
216
+
217
+ // assertions on owners/threshold
218
+ assertTrue(safe.isOwner(eve), "new owner not added");
219
+ assertFalse(safe.isOwner(bob), "old owner not removed");
220
+ assertEq(safe.getThreshold(), 2, "threshold changed unexpectedly");
221
+
222
+ // Attempt upgrade signed by removed owner should fail
223
+ bytes memory upg = abi.encodeWithSelector(
224
+ IUUPS.upgradeToAndCall.selector,
225
+ address(v2Impl),
226
+ ""
227
+ );
228
+ bytes32 upgHash = _txHash(safe, incoProxyAddr, 0, upg);
229
+ bytes memory badSigA = _sign(alicePrivKey, upgHash);
230
+ bytes memory badSigB = _sign(bobPrivKey, upgHash); // removed owner
231
+ bytes memory badSigs = _packSortedTwo(badSigA, alice, badSigB, bob);
232
+ vm.expectRevert();
233
+ _execSafe(safe, incoProxyAddr, 0, upg, badSigs);
234
+
235
+ // Now sign with new owner (owner4) and succeed
236
+ bytes memory goodSigA = _sign(alicePrivKey, upgHash);
237
+ bytes memory goodSigE = _sign(evePrivKey, upgHash);
238
+ bytes memory goodSigs = _packSortedTwo(goodSigA, alice, goodSigE, eve);
239
+ _execSafe(safe, incoProxyAddr, 0, upg, goodSigs);
240
+
241
+ address implAfter = address(
242
+ uint160(uint256(vm.load(incoProxyAddr, IMPLEMENTATION_SLOT)))
243
+ );
244
+ assertEq(implAfter, address(v2Impl));
245
+
246
+ string memory versionAfter = inco.getVersion();
247
+ assertEq(versionAfter, "255_255_255");
248
+ }
249
+
250
+ function test_Upgrade_NotBy_SafeWallet_Fails() public {
251
+ vm.prank(owner);
252
+ vm.expectRevert();
253
+ inco.upgradeToAndCall(address(v2Impl), "");
254
+ }
255
+
256
+ // Helpers
257
+ function _txHash(
258
+ Safe _safe,
259
+ address to,
260
+ uint256 value,
261
+ bytes memory data
262
+ ) internal view returns (bytes32) {
263
+ return
264
+ _safe.getTransactionHash(
265
+ to,
266
+ value,
267
+ data,
268
+ Enum.Operation.Call,
269
+ 0,
270
+ 0,
271
+ 0,
272
+ address(0),
273
+ payable(address(0)),
274
+ _safe.nonce()
275
+ );
276
+ }
277
+ function _sign(
278
+ uint256 pk,
279
+ bytes32 txHash
280
+ ) internal view returns (bytes memory) {
281
+ (uint8 v, bytes32 r, bytes32 s) = vm.sign(pk, txHash);
282
+ if (v < 27) v += 27;
283
+ return abi.encodePacked(r, s, v);
284
+ }
285
+ function _packSortedTwo(
286
+ bytes memory sigA,
287
+ address addrA,
288
+ bytes memory sigB,
289
+ address addrB
290
+ ) internal pure returns (bytes memory) {
291
+ return
292
+ addrA < addrB ? bytes.concat(sigA, sigB) : bytes.concat(sigB, sigA);
293
+ }
294
+ function _execSafe(
295
+ Safe _safe,
296
+ address to,
297
+ uint256 value,
298
+ bytes memory data,
299
+ bytes memory signatures
300
+ ) internal {
301
+ _safe.execTransaction(
302
+ to,
303
+ value,
304
+ data,
305
+ Enum.Operation.Call,
306
+ 0,
307
+ 0,
308
+ 0,
309
+ address(0),
310
+ payable(address(0)),
311
+ signatures
312
+ );
313
+ }
314
+ }