@inco/lightning 0.8.0-devnet-2 → 0.8.0-devnet-3
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/README.md +23 -67
- package/package.json +1 -1
- package/src/lightning-parts/AccessControl/test/TestAdvancedAccessControl.t.sol +75 -1
- package/src/lightning-parts/AccessControl/test/TestBaseAccessControl.t.sol +245 -0
- package/src/lightning-parts/EncryptedOperations.sol +1 -5
- package/src/lightning-parts/TrivialEncryption.sol +29 -0
- package/src/lightning-parts/primitives/SignatureVerifier.sol +42 -11
- package/src/lightning-parts/primitives/test/SignatureVerifier.t.sol +454 -29
- package/src/lightning-parts/test/HandleMetadata.t.sol +335 -1
- package/src/periphery/SessionVerifier.sol +3 -1
- package/src/test/TEELifecycle/TEELifecycleMockTest.t.sol +231 -1
- package/src/test/TestEventCounter.t.sol +43 -0
- package/src/test/TestFakeInfra.t.sol +9 -2
- package/src/test/TestFeeWithdrawal.t.sol +0 -1
- package/src/test/TestIncoUtils.t.sol +50 -0
- package/src/test/TestLib.t.sol +794 -0
- package/src/test/TestUpgrade.t.sol +114 -0
- package/src/test/TestVersion.t.sol +1 -0
|
@@ -212,7 +212,7 @@ contract TestSignatureVerifier is TestUtils, SignatureVerifier {
|
|
|
212
212
|
assertFalse(isValidSignature(digest, signatures));
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
function
|
|
215
|
+
function testDuplicateSignerReturnsFalse() public {
|
|
216
216
|
exposedAddSigner(alice);
|
|
217
217
|
exposedAddSigner(bob);
|
|
218
218
|
exposedSetThreshold(2);
|
|
@@ -223,19 +223,11 @@ contract TestSignatureVerifier is TestUtils, SignatureVerifier {
|
|
|
223
223
|
privKeys[1] = alicePrivKey; // Duplicate
|
|
224
224
|
bytes[] memory signatures = getSortedSignatures(digest, privKeys);
|
|
225
225
|
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
try this.isValidSignature(digest, signatures) returns (bool result) {
|
|
229
|
-
assertFalse(result, "Expected signature validation to fail with duplicates");
|
|
230
|
-
} catch (bytes memory reason) {
|
|
231
|
-
// Verify it's the correct error
|
|
232
|
-
bytes4 expectedSelector = SignatureVerifier.SignersNotInAscendingOrder.selector;
|
|
233
|
-
bytes4 receivedSelector = bytes4(reason);
|
|
234
|
-
assertEq(receivedSelector, expectedSelector, "Wrong error selector");
|
|
235
|
-
}
|
|
226
|
+
// Duplicate signers should return false (not revert)
|
|
227
|
+
assertFalse(isValidSignature(digest, signatures), "Duplicate signer should return false");
|
|
236
228
|
}
|
|
237
229
|
|
|
238
|
-
function
|
|
230
|
+
function testUnsortedSignaturesStillWork() public {
|
|
239
231
|
exposedAddSigner(alice);
|
|
240
232
|
exposedAddSigner(bob);
|
|
241
233
|
exposedAddSigner(carol);
|
|
@@ -259,17 +251,11 @@ contract TestSignatureVerifier is TestUtils, SignatureVerifier {
|
|
|
259
251
|
signatures[1] = getSignatureForDigest(digest, bobPrivKey);
|
|
260
252
|
}
|
|
261
253
|
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
fail("Expected revert for signatures not in ascending order");
|
|
265
|
-
} catch (bytes memory reason) {
|
|
266
|
-
bytes4 expectedSelector = SignatureVerifier.SignersNotInAscendingOrder.selector;
|
|
267
|
-
bytes4 receivedSelector = bytes4(reason);
|
|
268
|
-
assertEq(receivedSelector, expectedSelector, "Wrong error selector");
|
|
269
|
-
}
|
|
254
|
+
// Unsorted signatures should now work (optimistic sorting pattern)
|
|
255
|
+
assertTrue(isValidSignature(digest, signatures), "Unsorted signatures should work");
|
|
270
256
|
}
|
|
271
257
|
|
|
272
|
-
function
|
|
258
|
+
function testThreeUnsortedSignaturesStillWork() public {
|
|
273
259
|
exposedAddSigner(alice);
|
|
274
260
|
exposedAddSigner(bob);
|
|
275
261
|
exposedAddSigner(carol);
|
|
@@ -337,14 +323,8 @@ contract TestSignatureVerifier is TestUtils, SignatureVerifier {
|
|
|
337
323
|
}
|
|
338
324
|
}
|
|
339
325
|
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
fail("Expected revert for signatures not in ascending order");
|
|
343
|
-
} catch (bytes memory reason) {
|
|
344
|
-
bytes4 expectedSelector = SignatureVerifier.SignersNotInAscendingOrder.selector;
|
|
345
|
-
bytes4 receivedSelector = bytes4(reason);
|
|
346
|
-
assertEq(receivedSelector, expectedSelector, "Wrong error selector");
|
|
347
|
-
}
|
|
326
|
+
// Unsorted signatures should now work (optimistic sorting pattern)
|
|
327
|
+
assertTrue(isValidSignature(digest, signatures), "Unsorted signatures should work");
|
|
348
328
|
}
|
|
349
329
|
|
|
350
330
|
function testSignaturesInCorrectAscendingOrderPasses() public {
|
|
@@ -835,4 +815,449 @@ contract TestSignatureVerifier is TestUtils, SignatureVerifier {
|
|
|
835
815
|
assertFalse(isValidSignature(digest, sigs));
|
|
836
816
|
}
|
|
837
817
|
|
|
818
|
+
// ============ Tests for Malformed Signatures ============
|
|
819
|
+
|
|
820
|
+
function testMalformedSignatureIsSkipped() public {
|
|
821
|
+
exposedAddSigner(alice);
|
|
822
|
+
exposedAddSigner(bob);
|
|
823
|
+
exposedSetThreshold(2);
|
|
824
|
+
|
|
825
|
+
bytes32 digest = keccak256("test message");
|
|
826
|
+
|
|
827
|
+
// Create valid signatures
|
|
828
|
+
uint256[] memory privKeys = new uint256[](2);
|
|
829
|
+
privKeys[0] = alicePrivKey;
|
|
830
|
+
privKeys[1] = bobPrivKey;
|
|
831
|
+
bytes[] memory validSigs = getSortedSignatures(digest, privKeys);
|
|
832
|
+
|
|
833
|
+
// Create array with malformed signature in the middle
|
|
834
|
+
bytes[] memory signatures = new bytes[](3);
|
|
835
|
+
signatures[0] = validSigs[0];
|
|
836
|
+
signatures[1] = hex"deadbeef"; // Malformed signature
|
|
837
|
+
signatures[2] = validSigs[1];
|
|
838
|
+
|
|
839
|
+
// Should still pass - malformed signature is skipped
|
|
840
|
+
assertTrue(isValidSignature(digest, signatures), "Malformed signature should be skipped");
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function testAllMalformedSignaturesFails() public {
|
|
844
|
+
exposedAddSigner(alice);
|
|
845
|
+
exposedSetThreshold(1);
|
|
846
|
+
|
|
847
|
+
bytes32 digest = keccak256("test message");
|
|
848
|
+
|
|
849
|
+
bytes[] memory signatures = new bytes[](2);
|
|
850
|
+
signatures[0] = hex"deadbeef";
|
|
851
|
+
signatures[1] = hex"cafebabe";
|
|
852
|
+
|
|
853
|
+
// Should fail - no valid signatures
|
|
854
|
+
assertFalse(isValidSignature(digest, signatures), "All malformed should fail");
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function testEmptySignatureIsSkipped() public {
|
|
858
|
+
exposedAddSigner(alice);
|
|
859
|
+
exposedAddSigner(bob);
|
|
860
|
+
exposedSetThreshold(2);
|
|
861
|
+
|
|
862
|
+
bytes32 digest = keccak256("test message");
|
|
863
|
+
|
|
864
|
+
uint256[] memory privKeys = new uint256[](2);
|
|
865
|
+
privKeys[0] = alicePrivKey;
|
|
866
|
+
privKeys[1] = bobPrivKey;
|
|
867
|
+
bytes[] memory validSigs = getSortedSignatures(digest, privKeys);
|
|
868
|
+
|
|
869
|
+
// Create array with empty signature
|
|
870
|
+
bytes[] memory signatures = new bytes[](3);
|
|
871
|
+
signatures[0] = validSigs[0];
|
|
872
|
+
signatures[1] = ""; // Empty signature
|
|
873
|
+
signatures[2] = validSigs[1];
|
|
874
|
+
|
|
875
|
+
// Should pass - empty signature is skipped
|
|
876
|
+
assertTrue(isValidSignature(digest, signatures), "Empty signature should be skipped");
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function testUnsortedSignaturesWithMalformedInMiddle() public {
|
|
880
|
+
exposedAddSigner(alice);
|
|
881
|
+
exposedAddSigner(bob);
|
|
882
|
+
exposedSetThreshold(2);
|
|
883
|
+
|
|
884
|
+
bytes32 digest = keccak256("test message");
|
|
885
|
+
|
|
886
|
+
// Get individual signatures
|
|
887
|
+
bytes memory aliceSig = getSignatureForDigest(digest, alicePrivKey);
|
|
888
|
+
bytes memory bobSig = getSignatureForDigest(digest, bobPrivKey);
|
|
889
|
+
|
|
890
|
+
// Determine addresses to create descending order
|
|
891
|
+
address aliceAddr = vm.addr(alicePrivKey);
|
|
892
|
+
address bobAddr = vm.addr(bobPrivKey);
|
|
893
|
+
|
|
894
|
+
// Create array: [higherAddr, malformed, lowerAddr] - descending order with invalid in middle
|
|
895
|
+
bytes[] memory signatures = new bytes[](3);
|
|
896
|
+
if (aliceAddr > bobAddr) {
|
|
897
|
+
// Alice > Bob, so put Alice first (descending)
|
|
898
|
+
signatures[0] = aliceSig;
|
|
899
|
+
signatures[1] = hex"deadbeef"; // Malformed signature in middle
|
|
900
|
+
signatures[2] = bobSig;
|
|
901
|
+
} else {
|
|
902
|
+
// Bob > Alice, so put Bob first (descending)
|
|
903
|
+
signatures[0] = bobSig;
|
|
904
|
+
signatures[1] = hex"deadbeef"; // Malformed signature in middle
|
|
905
|
+
signatures[2] = aliceSig;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// Should pass: unsorted valid signatures with malformed skipped
|
|
909
|
+
// This tests that fallback duplicate detection works correctly when
|
|
910
|
+
// malformed signatures create gaps in processing
|
|
911
|
+
assertTrue(isValidSignature(digest, signatures), "Unsorted with malformed in middle should work");
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// ============ Tests for validCount tracking ============
|
|
915
|
+
|
|
916
|
+
/// @notice Tests that validCount correctly tracks only valid signatures, not array indices
|
|
917
|
+
/// @dev This is critical for the duplicate detection fallback loop which iterates over validCount
|
|
918
|
+
function testValidCountWithMultipleMalformedSignatures() public {
|
|
919
|
+
exposedAddSigner(alice);
|
|
920
|
+
exposedAddSigner(bob);
|
|
921
|
+
exposedAddSigner(carol);
|
|
922
|
+
exposedSetThreshold(3);
|
|
923
|
+
|
|
924
|
+
bytes32 digest = keccak256("test message");
|
|
925
|
+
|
|
926
|
+
// Get sorted valid signatures
|
|
927
|
+
uint256[] memory privKeys = new uint256[](3);
|
|
928
|
+
privKeys[0] = alicePrivKey;
|
|
929
|
+
privKeys[1] = bobPrivKey;
|
|
930
|
+
privKeys[2] = carolPrivKey;
|
|
931
|
+
bytes[] memory validSigs = getSortedSignatures(digest, privKeys);
|
|
932
|
+
|
|
933
|
+
// Create array with multiple malformed signatures interspersed
|
|
934
|
+
// [malformed, valid0, malformed, malformed, valid1, malformed, valid2]
|
|
935
|
+
bytes[] memory signatures = new bytes[](7);
|
|
936
|
+
signatures[0] = hex"dead";
|
|
937
|
+
signatures[1] = validSigs[0];
|
|
938
|
+
signatures[2] = hex"beef";
|
|
939
|
+
signatures[3] = hex"cafe";
|
|
940
|
+
signatures[4] = validSigs[1];
|
|
941
|
+
signatures[5] = hex"babe";
|
|
942
|
+
signatures[6] = validSigs[2];
|
|
943
|
+
|
|
944
|
+
// Should pass: validCount should correctly track 3 valid signatures
|
|
945
|
+
// despite 4 malformed ones creating gaps in the array
|
|
946
|
+
assertTrue(isValidSignature(digest, signatures), "Should work with interspersed malformed signatures");
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/// @notice Tests duplicate detection works when duplicates are separated by malformed signatures
|
|
950
|
+
/// @dev Validates that validCount-based iteration catches duplicates even with gaps
|
|
951
|
+
function testDuplicateDetectionWithMalformedGaps() public {
|
|
952
|
+
exposedAddSigner(alice);
|
|
953
|
+
exposedAddSigner(bob);
|
|
954
|
+
exposedSetThreshold(2);
|
|
955
|
+
|
|
956
|
+
bytes32 digest = keccak256("test message");
|
|
957
|
+
|
|
958
|
+
bytes memory aliceSig = getSignatureForDigest(digest, alicePrivKey);
|
|
959
|
+
|
|
960
|
+
// [alice, malformed, malformed, alice] - duplicate with gaps
|
|
961
|
+
bytes[] memory signatures = new bytes[](4);
|
|
962
|
+
signatures[0] = aliceSig;
|
|
963
|
+
signatures[1] = hex"dead";
|
|
964
|
+
signatures[2] = hex"beef";
|
|
965
|
+
signatures[3] = aliceSig; // Duplicate
|
|
966
|
+
|
|
967
|
+
// Should fail: duplicate alice signature should be detected
|
|
968
|
+
// even though malformed signatures create gaps in recoveredSigners
|
|
969
|
+
assertFalse(isValidSignature(digest, signatures), "Should detect duplicate with malformed gaps");
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/// @notice Tests that unsorted duplicates are detected via fallback loop with malformed gaps
|
|
973
|
+
/// @dev Combines unsorted order + duplicates + malformed signatures
|
|
974
|
+
function testUnsortedDuplicateWithMalformedGaps() public {
|
|
975
|
+
exposedAddSigner(alice);
|
|
976
|
+
exposedAddSigner(bob);
|
|
977
|
+
exposedAddSigner(carol);
|
|
978
|
+
exposedSetThreshold(3);
|
|
979
|
+
|
|
980
|
+
bytes32 digest = keccak256("test message");
|
|
981
|
+
|
|
982
|
+
bytes memory aliceSig = getSignatureForDigest(digest, alicePrivKey);
|
|
983
|
+
bytes memory bobSig = getSignatureForDigest(digest, bobPrivKey);
|
|
984
|
+
|
|
985
|
+
address aliceAddr = vm.addr(alicePrivKey);
|
|
986
|
+
address bobAddr = vm.addr(bobPrivKey);
|
|
987
|
+
|
|
988
|
+
// Create: [higher, malformed, lower, malformed, higher(duplicate)]
|
|
989
|
+
// This forces fallback path AND tests duplicate detection across gaps
|
|
990
|
+
bytes[] memory signatures = new bytes[](5);
|
|
991
|
+
if (aliceAddr > bobAddr) {
|
|
992
|
+
signatures[0] = aliceSig; // Higher first (will set lastSigner)
|
|
993
|
+
signatures[1] = hex"dead";
|
|
994
|
+
signatures[2] = bobSig; // Lower (triggers fallback)
|
|
995
|
+
signatures[3] = hex"beef";
|
|
996
|
+
signatures[4] = aliceSig; // Duplicate of signatures[0]
|
|
997
|
+
} else {
|
|
998
|
+
signatures[0] = bobSig;
|
|
999
|
+
signatures[1] = hex"dead";
|
|
1000
|
+
signatures[2] = aliceSig;
|
|
1001
|
+
signatures[3] = hex"beef";
|
|
1002
|
+
signatures[4] = bobSig; // Duplicate
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// Should fail: duplicate should be caught by fallback loop
|
|
1006
|
+
assertFalse(isValidSignature(digest, signatures), "Should detect unsorted duplicate with gaps");
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/// @notice Tests edge case where all signatures before valid ones are malformed
|
|
1010
|
+
function testAllMalformedBeforeValid() public {
|
|
1011
|
+
exposedAddSigner(alice);
|
|
1012
|
+
exposedAddSigner(bob);
|
|
1013
|
+
exposedSetThreshold(2);
|
|
1014
|
+
|
|
1015
|
+
bytes32 digest = keccak256("test message");
|
|
1016
|
+
|
|
1017
|
+
uint256[] memory privKeys = new uint256[](2);
|
|
1018
|
+
privKeys[0] = alicePrivKey;
|
|
1019
|
+
privKeys[1] = bobPrivKey;
|
|
1020
|
+
bytes[] memory validSigs = getSortedSignatures(digest, privKeys);
|
|
1021
|
+
|
|
1022
|
+
// [malformed, malformed, malformed, valid0, valid1]
|
|
1023
|
+
bytes[] memory signatures = new bytes[](5);
|
|
1024
|
+
signatures[0] = hex"11";
|
|
1025
|
+
signatures[1] = hex"22";
|
|
1026
|
+
signatures[2] = hex"33";
|
|
1027
|
+
signatures[3] = validSigs[0];
|
|
1028
|
+
signatures[4] = validSigs[1];
|
|
1029
|
+
|
|
1030
|
+
// Should pass: validCount starts at 0, first valid signature at index 3
|
|
1031
|
+
// becomes recoveredSigners[0], second at index 4 becomes recoveredSigners[1]
|
|
1032
|
+
assertTrue(isValidSignature(digest, signatures), "Should work with leading malformed signatures");
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// ============ Tests for getSignerAtIndex and getSignersCount ============
|
|
1036
|
+
|
|
1037
|
+
function testGetSignersCount() public {
|
|
1038
|
+
assertEq(this.getSignersCount(), 0);
|
|
1039
|
+
|
|
1040
|
+
exposedAddSigner(alice);
|
|
1041
|
+
assertEq(this.getSignersCount(), 1);
|
|
1042
|
+
|
|
1043
|
+
exposedAddSigner(bob);
|
|
1044
|
+
assertEq(this.getSignersCount(), 2);
|
|
1045
|
+
|
|
1046
|
+
exposedAddSigner(carol);
|
|
1047
|
+
assertEq(this.getSignersCount(), 3);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
function testGetSignerAtIndex() public {
|
|
1051
|
+
exposedAddSigner(alice);
|
|
1052
|
+
exposedAddSigner(bob);
|
|
1053
|
+
exposedAddSigner(carol);
|
|
1054
|
+
|
|
1055
|
+
assertEq(this.getSignerAtIndex(0), alice);
|
|
1056
|
+
assertEq(this.getSignerAtIndex(1), bob);
|
|
1057
|
+
assertEq(this.getSignerAtIndex(2), carol);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
function testGetSignerAtIndexAfterRemoval() public {
|
|
1061
|
+
exposedAddSigner(alice);
|
|
1062
|
+
exposedAddSigner(bob);
|
|
1063
|
+
exposedAddSigner(carol);
|
|
1064
|
+
exposedSetThreshold(1);
|
|
1065
|
+
|
|
1066
|
+
// Remove bob (middle element) - should move carol to bob's position
|
|
1067
|
+
exposedRemoveSigner(bob);
|
|
1068
|
+
|
|
1069
|
+
assertEq(this.getSignersCount(), 2);
|
|
1070
|
+
assertEq(this.getSignerAtIndex(0), alice);
|
|
1071
|
+
assertEq(this.getSignerAtIndex(1), carol); // carol moved to index 1
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/// @dev Test that getSignerAtIndex reverts when accessing out of bounds index
|
|
1075
|
+
function testGetSignerAtIndexOutOfBounds() public {
|
|
1076
|
+
// Empty signers array - any index should revert
|
|
1077
|
+
vm.expectRevert();
|
|
1078
|
+
this.getSignerAtIndex(0);
|
|
1079
|
+
|
|
1080
|
+
// Add one signer, then try to access index 1
|
|
1081
|
+
exposedAddSigner(alice);
|
|
1082
|
+
vm.expectRevert();
|
|
1083
|
+
this.getSignerAtIndex(1);
|
|
1084
|
+
|
|
1085
|
+
// Add more signers, then try to access beyond the count
|
|
1086
|
+
exposedAddSigner(bob);
|
|
1087
|
+
exposedAddSigner(carol);
|
|
1088
|
+
vm.expectRevert();
|
|
1089
|
+
this.getSignerAtIndex(3);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
function testGetThreshold() public {
|
|
1093
|
+
exposedAddSigner(alice);
|
|
1094
|
+
exposedAddSigner(bob);
|
|
1095
|
+
exposedSetThreshold(2);
|
|
1096
|
+
|
|
1097
|
+
assertEq(this.getThreshold(), 2);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
function testIsSigner() public {
|
|
1101
|
+
assertFalse(this.isSigner(alice));
|
|
1102
|
+
|
|
1103
|
+
exposedAddSigner(alice);
|
|
1104
|
+
assertTrue(this.isSigner(alice));
|
|
1105
|
+
assertFalse(this.isSigner(bob));
|
|
1106
|
+
|
|
1107
|
+
exposedAddSigner(bob);
|
|
1108
|
+
assertTrue(this.isSigner(bob));
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// ============ Fuzz Tests for Signature Verification ============
|
|
1112
|
+
|
|
1113
|
+
/// @dev Fuzz test for threshold validation with varying number of signers
|
|
1114
|
+
/// Tests that signatures meeting the threshold return true
|
|
1115
|
+
function testFuzzValidSignaturesWithVaryingThreshold(uint8 numSigners, uint8 threshold) public {
|
|
1116
|
+
// Constrain inputs to reasonable values
|
|
1117
|
+
numSigners = uint8(bound(numSigners, 1, 10));
|
|
1118
|
+
threshold = uint8(bound(threshold, 1, numSigners));
|
|
1119
|
+
|
|
1120
|
+
// Generate signers with unique private keys
|
|
1121
|
+
uint256[] memory privKeys = new uint256[](numSigners);
|
|
1122
|
+
for (uint256 i = 0; i < numSigners; i++) {
|
|
1123
|
+
privKeys[i] = uint256(keccak256(abi.encodePacked("signer", i))) % (type(uint256).max - 1) + 1;
|
|
1124
|
+
address signerAddr = vm.addr(privKeys[i]);
|
|
1125
|
+
exposedAddSigner(signerAddr);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
exposedSetThreshold(threshold);
|
|
1129
|
+
|
|
1130
|
+
// Create signatures from exactly threshold number of signers
|
|
1131
|
+
bytes32 digest = keccak256("fuzz test message");
|
|
1132
|
+
uint256[] memory signingKeys = new uint256[](threshold);
|
|
1133
|
+
for (uint256 i = 0; i < threshold; i++) {
|
|
1134
|
+
signingKeys[i] = privKeys[i];
|
|
1135
|
+
}
|
|
1136
|
+
bytes[] memory signatures = getSortedSignatures(digest, signingKeys);
|
|
1137
|
+
|
|
1138
|
+
assertTrue(isValidSignature(digest, signatures), "Valid signatures meeting threshold should pass");
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/// @dev Fuzz test that signatures below threshold return false
|
|
1142
|
+
function testFuzzInsufficientSignatures(uint8 numSigners, uint8 threshold, uint8 sigCount) public {
|
|
1143
|
+
// Constrain inputs
|
|
1144
|
+
numSigners = uint8(bound(numSigners, 2, 10));
|
|
1145
|
+
threshold = uint8(bound(threshold, 2, numSigners));
|
|
1146
|
+
sigCount = uint8(bound(sigCount, 1, threshold - 1)); // Always below threshold
|
|
1147
|
+
|
|
1148
|
+
// Generate signers
|
|
1149
|
+
uint256[] memory privKeys = new uint256[](numSigners);
|
|
1150
|
+
for (uint256 i = 0; i < numSigners; i++) {
|
|
1151
|
+
privKeys[i] = uint256(keccak256(abi.encodePacked("signer_insuf", i))) % (type(uint256).max - 1) + 1;
|
|
1152
|
+
address signerAddr = vm.addr(privKeys[i]);
|
|
1153
|
+
exposedAddSigner(signerAddr);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
exposedSetThreshold(threshold);
|
|
1157
|
+
|
|
1158
|
+
// Create fewer signatures than threshold
|
|
1159
|
+
bytes32 digest = keccak256("fuzz insufficient test");
|
|
1160
|
+
uint256[] memory signingKeys = new uint256[](sigCount);
|
|
1161
|
+
for (uint256 i = 0; i < sigCount; i++) {
|
|
1162
|
+
signingKeys[i] = privKeys[i];
|
|
1163
|
+
}
|
|
1164
|
+
bytes[] memory signatures = getSortedSignatures(digest, signingKeys);
|
|
1165
|
+
|
|
1166
|
+
assertFalse(isValidSignature(digest, signatures), "Insufficient signatures should fail");
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/// @dev Fuzz test that more signatures than threshold still passes
|
|
1170
|
+
function testFuzzExcessSignatures(uint8 numSigners, uint8 threshold) public {
|
|
1171
|
+
// Constrain inputs - need at least one extra signature beyond threshold
|
|
1172
|
+
numSigners = uint8(bound(numSigners, 2, 10));
|
|
1173
|
+
threshold = uint8(bound(threshold, 1, numSigners - 1)); // At least one extra signer
|
|
1174
|
+
|
|
1175
|
+
// Generate signers
|
|
1176
|
+
uint256[] memory privKeys = new uint256[](numSigners);
|
|
1177
|
+
for (uint256 i = 0; i < numSigners; i++) {
|
|
1178
|
+
privKeys[i] = uint256(keccak256(abi.encodePacked("signer_excess", i))) % (type(uint256).max - 1) + 1;
|
|
1179
|
+
address signerAddr = vm.addr(privKeys[i]);
|
|
1180
|
+
exposedAddSigner(signerAddr);
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
exposedSetThreshold(threshold);
|
|
1184
|
+
|
|
1185
|
+
// Create signatures from ALL signers (more than threshold)
|
|
1186
|
+
bytes32 digest = keccak256("fuzz excess test");
|
|
1187
|
+
bytes[] memory signatures = getSortedSignatures(digest, privKeys);
|
|
1188
|
+
|
|
1189
|
+
assertTrue(isValidSignature(digest, signatures), "Excess valid signatures should pass");
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/// @dev Fuzz test that threshold equal to number of signers works correctly
|
|
1193
|
+
function testFuzzThresholdEqualsSignerCount(uint8 numSigners) public {
|
|
1194
|
+
numSigners = uint8(bound(numSigners, 1, 10));
|
|
1195
|
+
|
|
1196
|
+
// Generate signers
|
|
1197
|
+
uint256[] memory privKeys = new uint256[](numSigners);
|
|
1198
|
+
for (uint256 i = 0; i < numSigners; i++) {
|
|
1199
|
+
privKeys[i] = uint256(keccak256(abi.encodePacked("signer_equal", i))) % (type(uint256).max - 1) + 1;
|
|
1200
|
+
address signerAddr = vm.addr(privKeys[i]);
|
|
1201
|
+
exposedAddSigner(signerAddr);
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
// Set threshold equal to signer count
|
|
1205
|
+
exposedSetThreshold(numSigners);
|
|
1206
|
+
|
|
1207
|
+
bytes32 digest = keccak256("fuzz equal threshold test");
|
|
1208
|
+
bytes[] memory signatures = getSortedSignatures(digest, privKeys);
|
|
1209
|
+
|
|
1210
|
+
assertTrue(isValidSignature(digest, signatures), "All signers signing should pass when threshold equals count");
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/// @dev Fuzz test that mixing valid and invalid signers correctly counts only valid ones
|
|
1214
|
+
function testFuzzMixedValidInvalidSigners(uint8 numValidSigners, uint8 numInvalidSigners, uint8 threshold) public {
|
|
1215
|
+
// Constrain inputs
|
|
1216
|
+
numValidSigners = uint8(bound(numValidSigners, 1, 5));
|
|
1217
|
+
numInvalidSigners = uint8(bound(numInvalidSigners, 1, 5));
|
|
1218
|
+
threshold = uint8(bound(threshold, 1, numValidSigners)); // Must be achievable with valid signers
|
|
1219
|
+
|
|
1220
|
+
// Generate and register valid signers
|
|
1221
|
+
uint256[] memory validPrivKeys = new uint256[](numValidSigners);
|
|
1222
|
+
for (uint256 i = 0; i < numValidSigners; i++) {
|
|
1223
|
+
validPrivKeys[i] = uint256(keccak256(abi.encodePacked("valid_signer", i))) % (type(uint256).max - 1) + 1;
|
|
1224
|
+
address signerAddr = vm.addr(validPrivKeys[i]);
|
|
1225
|
+
exposedAddSigner(signerAddr);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
exposedSetThreshold(threshold);
|
|
1229
|
+
|
|
1230
|
+
// Generate invalid signer keys (not registered)
|
|
1231
|
+
uint256[] memory invalidPrivKeys = new uint256[](numInvalidSigners);
|
|
1232
|
+
for (uint256 i = 0; i < numInvalidSigners; i++) {
|
|
1233
|
+
invalidPrivKeys[i] = uint256(keccak256(abi.encodePacked("invalid_signer", i))) % (type(uint256).max - 1) + 1;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// Create signatures from all valid signers (should pass regardless of invalid ones)
|
|
1237
|
+
bytes32 digest = keccak256("fuzz mixed signers test");
|
|
1238
|
+
bytes[] memory signatures = getSortedSignatures(digest, validPrivKeys);
|
|
1239
|
+
|
|
1240
|
+
assertTrue(isValidSignature(digest, signatures), "Valid signatures meeting threshold should pass");
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
/// @dev Fuzz test for digest variation - same signers, different messages
|
|
1244
|
+
function testFuzzDifferentDigests(bytes32 digest, uint8 numSigners) public {
|
|
1245
|
+
numSigners = uint8(bound(numSigners, 1, 5));
|
|
1246
|
+
|
|
1247
|
+
// Generate signers
|
|
1248
|
+
uint256[] memory privKeys = new uint256[](numSigners);
|
|
1249
|
+
for (uint256 i = 0; i < numSigners; i++) {
|
|
1250
|
+
privKeys[i] = uint256(keccak256(abi.encodePacked("digest_signer", i))) % (type(uint256).max - 1) + 1;
|
|
1251
|
+
address signerAddr = vm.addr(privKeys[i]);
|
|
1252
|
+
exposedAddSigner(signerAddr);
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
exposedSetThreshold(numSigners);
|
|
1256
|
+
|
|
1257
|
+
// Sign the fuzzed digest
|
|
1258
|
+
bytes[] memory signatures = getSortedSignatures(digest, privKeys);
|
|
1259
|
+
|
|
1260
|
+
assertTrue(isValidSignature(digest, signatures), "Valid signatures should pass for any digest");
|
|
1261
|
+
}
|
|
1262
|
+
|
|
838
1263
|
}
|