@coti-io/coti-contracts 1.0.8 → 1.1.0

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 (36) hide show
  1. package/contracts/mocks/utils/mpc/Arithmetic128BitTestsContract.sol +260 -0
  2. package/contracts/mocks/utils/mpc/Arithmetic256BitTestsContract.sol +264 -0
  3. package/contracts/mocks/utils/mpc/Bitwise128BitTestsContract.sol +91 -0
  4. package/contracts/mocks/utils/mpc/Bitwise256BitTestsContract.sol +91 -0
  5. package/contracts/mocks/utils/mpc/Comparison128BitTestsContract.sol +158 -0
  6. package/contracts/mocks/utils/mpc/Comparison256BitTestsContract.sol +158 -0
  7. package/contracts/mocks/utils/mpc/Miscellaneous128BitTestsContract.sol +185 -0
  8. package/contracts/mocks/utils/mpc/Miscellaneous256BitTestsContract.sol +190 -0
  9. package/contracts/utils/mpc/MpcCore.sol +2804 -180
  10. package/hardhat.config.ts +6 -2
  11. package/package.json +2 -2
  12. package/test/token/PrivateERC20/PrivateERC20.test.ts +3 -3
  13. package/test/utils/mpc/Unsigned128BitIntegers.test.ts +945 -0
  14. package/test/utils/mpc/Unsigned256BitIntegers.test.ts +1124 -0
  15. package/test/utils/mpc/helpers.ts +77 -0
  16. package/typechain-types/contracts/mocks/utils/mpc/Arithmetic128BitTestsContract.ts +341 -0
  17. package/typechain-types/contracts/mocks/utils/mpc/Arithmetic256BitTestsContract.ts +347 -0
  18. package/typechain-types/contracts/mocks/utils/mpc/Bitwise128BitTestsContract.ts +186 -0
  19. package/typechain-types/contracts/mocks/utils/mpc/Bitwise256BitTestsContract.ts +186 -0
  20. package/typechain-types/contracts/mocks/utils/mpc/Comparison128BitTestsContract.ts +260 -0
  21. package/typechain-types/contracts/mocks/utils/mpc/Comparison256BitTestsContract.ts +260 -0
  22. package/typechain-types/contracts/mocks/utils/mpc/Miscellaneous128BitTestsContract.ts +302 -0
  23. package/typechain-types/contracts/mocks/utils/mpc/Miscellaneous256BitTestsContract.ts +322 -0
  24. package/typechain-types/contracts/mocks/utils/mpc/index.ts +8 -0
  25. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic128BitTestsContract__factory.ts +349 -0
  26. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic256BitTestsContract__factory.ts +364 -0
  27. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise128BitTestsContract__factory.ts +182 -0
  28. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise256BitTestsContract__factory.ts +182 -0
  29. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison128BitTestsContract__factory.ts +255 -0
  30. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison256BitTestsContract__factory.ts +255 -0
  31. package/typechain-types/factories/contracts/mocks/utils/mpc/Miscellaneous128BitTestsContract__factory.ts +332 -0
  32. package/typechain-types/factories/contracts/mocks/utils/mpc/Miscellaneous256BitTestsContract__factory.ts +380 -0
  33. package/typechain-types/factories/contracts/mocks/utils/mpc/index.ts +8 -0
  34. package/typechain-types/hardhat.d.ts +144 -0
  35. package/typechain-types/index.ts +16 -0
  36. package/contracts/package.json +0 -11
@@ -8,6 +8,18 @@ type gtUint16 is uint256;
8
8
  type gtUint32 is uint256;
9
9
  type gtUint64 is uint256;
10
10
 
11
+ // we use a struct because user-defined value types can only be elementary value types
12
+ struct gtUint128 {
13
+ gtUint64 high;
14
+ gtUint64 low;
15
+ }
16
+
17
+ // we use a struct because user-defined value types can only be elementary value types
18
+ struct gtUint256 {
19
+ gtUint128 high;
20
+ gtUint128 low;
21
+ }
22
+
11
23
  // we use a struct because user-defined value types can only be elementary value types
12
24
  // 8 characters (in byte form) per cell and the final cell padded with zeroes if needed
13
25
  struct gtString {
@@ -20,6 +32,18 @@ type ctUint16 is uint256;
20
32
  type ctUint32 is uint256;
21
33
  type ctUint64 is uint256;
22
34
 
35
+ // we use a struct because user-defined value types can only be elementary value types
36
+ struct ctUint128 {
37
+ ctUint64 high;
38
+ ctUint64 low;
39
+ }
40
+
41
+ // we use a struct because user-defined value types can only be elementary value types
42
+ struct ctUint256 {
43
+ ctUint128 high;
44
+ ctUint128 low;
45
+ }
46
+
23
47
  // we use a struct because user-defined value types can only be elementary value types
24
48
  // 8 characters (in byte form) per cell and the final cell padded with zeroes if needed
25
49
  struct ctString {
@@ -46,6 +70,14 @@ struct itUint64 {
46
70
  ctUint64 ciphertext;
47
71
  bytes signature;
48
72
  }
73
+ struct itUint128 {
74
+ ctUint128 ciphertext;
75
+ bytes[2] signature;
76
+ }
77
+ struct itUint256 {
78
+ ctUint256 ciphertext;
79
+ bytes[2][2] signature;
80
+ }
49
81
  struct itString {
50
82
  ctString ciphertext;
51
83
  bytes[] signature;
@@ -71,6 +103,14 @@ struct utUint64 {
71
103
  ctUint64 ciphertext;
72
104
  ctUint64 userCiphertext;
73
105
  }
106
+ struct utUint128 {
107
+ ctUint128 ciphertext;
108
+ ctUint128 userCiphertext;
109
+ }
110
+ struct utUint256 {
111
+ ctUint256 ciphertext;
112
+ ctUint256 userCiphertext;
113
+ }
74
114
  struct utString {
75
115
  ctString ciphertext;
76
116
  ctString userCiphertext;
@@ -998,288 +1038,1202 @@ library MpcCore {
998
1038
  }
999
1039
 
1000
1040
 
1001
- // =========== String operations ============
1041
+ // =========== 128 bit operations ============
1002
1042
 
1003
- function validateCiphertext(itString memory input) internal returns (gtString memory) {
1004
- uint256 len_ = input.signature.length;
1043
+ function _splitUint128(uint128 number) private returns (uint64, uint64) {
1044
+ return (uint64(number >> 64), uint64(number));
1045
+ }
1005
1046
 
1006
- require(input.ciphertext.value.length == len_, "MPC_CORE: INVALID_INPUT_TEXT");
1047
+ function validateCiphertext(itUint128 memory input) internal returns (gtUint128 memory) {
1048
+ gtUint128 memory result;
1049
+
1050
+ itUint64 memory highInput;
1051
+ highInput.ciphertext = input.ciphertext.high;
1052
+ highInput.signature = input.signature[0];
1053
+
1054
+ itUint64 memory lowInput;
1055
+ lowInput.ciphertext = input.ciphertext.low;
1056
+ lowInput.signature = input.signature[1];
1057
+
1058
+ result.high = validateCiphertext(highInput);
1059
+ result.low = validateCiphertext(lowInput);
1060
+
1061
+ return result;
1062
+ }
1007
1063
 
1008
- gtString memory gt_ = gtString(new gtUint64[](len_));
1064
+ function onBoard(ctUint128 memory ct) internal returns (gtUint128 memory) {
1065
+ gtUint128 memory result;
1009
1066
 
1010
- itUint64 memory it_;
1067
+ result.high = onBoard(ct.high);
1068
+ result.low = onBoard(ct.low);
1011
1069
 
1012
- for (uint256 i = 0; i < len_; ++i) {
1013
- it_.ciphertext = input.ciphertext.value[i];
1014
- it_.signature = input.signature[i];
1070
+ return result;
1071
+ }
1015
1072
 
1016
- gt_.value[i] = validateCiphertext(it_);
1017
- }
1073
+ function offBoard(gtUint128 memory pt) internal returns (ctUint128 memory) {
1074
+ ctUint128 memory result;
1018
1075
 
1019
- return gt_;
1020
- }
1076
+ result.high = offBoard(pt.high);
1077
+ result.low = offBoard(pt.low);
1021
1078
 
1022
- function onBoard(ctString memory ct) internal returns (gtString memory) {
1023
- uint256 len_ = ct.value.length;
1079
+ return result;
1080
+ }
1024
1081
 
1025
- gtString memory gt_ = gtString(new gtUint64[](len_));
1082
+ function offBoardToUser(gtUint128 memory pt, address addr) internal returns (ctUint128 memory) {
1083
+ ctUint128 memory result;
1026
1084
 
1027
- for (uint256 i = 0; i < len_; ++i) {
1028
- gt_.value[i] = onBoard(ct.value[i]);
1029
- }
1085
+ result.high = offBoardToUser(pt.high, addr);
1086
+ result.low = offBoardToUser(pt.low, addr);
1030
1087
 
1031
- return gt_;
1088
+ return result;
1032
1089
  }
1033
1090
 
1034
- function offBoard(gtString memory pt) internal returns (ctString memory) {
1035
- uint256 len_ = pt.value.length;
1036
-
1037
- ctString memory ct_ = ctString(new ctUint64[](len_));
1091
+ function offBoardCombined(gtUint128 memory pt, address addr) internal returns (utUint128 memory) {
1092
+ utUint128 memory result;
1038
1093
 
1039
- for (uint256 i = 0; i < len_; ++i) {
1040
- ct_.value[i] = offBoard(pt.value[i]);
1041
- }
1094
+ result.ciphertext = offBoard(pt);
1095
+ result.userCiphertext = offBoardToUser(pt, addr);
1042
1096
 
1043
- return ct_;
1097
+ return result;
1044
1098
  }
1045
1099
 
1046
- function offBoardToUser(gtString memory pt, address addr) internal returns (ctString memory) {
1047
- uint256 len_ = pt.value.length;
1100
+ function setPublic128(uint128 pt) internal returns (gtUint128 memory) {
1101
+ gtUint128 memory result;
1048
1102
 
1049
- ctString memory ct_ = ctString(new ctUint64[](len_));
1103
+ // Split the 128-bit value into high and low 64-bit parts
1104
+ uint64 low = uint64(pt);
1105
+ uint64 high = uint64(pt >> 64);
1106
+
1107
+ result.high = setPublic64(high);
1108
+ result.low = setPublic64(low);
1109
+
1110
+ return result;
1111
+ }
1050
1112
 
1051
- for (uint256 i = 0; i < len_; ++i) {
1052
- ct_.value[i] = offBoardToUser(pt.value[i], addr);
1053
- }
1113
+ function rand128() internal returns (gtUint128 memory) {
1114
+ gtUint128 memory result;
1054
1115
 
1055
- return ct_;
1056
- }
1116
+ result.high = rand64();
1117
+ result.low = rand64();
1057
1118
 
1058
- function offBoardCombined(gtString memory pt, address addr) internal returns (utString memory ut) {
1059
- ut.ciphertext = offBoard(pt);
1060
- ut.userCiphertext = offBoardToUser(pt, addr);
1119
+ return result;
1061
1120
  }
1062
1121
 
1063
- function setPublicString(string memory pt) internal returns (gtString memory) {
1064
- bytes memory strBytes_ = bytes(pt);
1065
- uint256 len_ = strBytes_.length;
1066
- uint256 count_ = (len_ + 7) / 8; // Number of bytes8 elements needed
1122
+ function randBoundedBits128(uint8 numBits) internal returns (gtUint128 memory) {
1123
+ gtUint128 memory result;
1067
1124
 
1068
- gtString memory result_ = gtString(new gtUint64[](count_));
1125
+ uint8 numBits_ = numBits > 64 ? 64 : numBits;
1069
1126
 
1070
- bytes8 cell_;
1127
+ result.low = randBoundedBits64(numBits_);
1071
1128
 
1072
- for (uint256 i = 0; i < count_ * 8; ++i) {
1073
- if (i % 8 == 0) {
1074
- cell_ = bytes8(0);
1075
- } else {
1076
- cell_ <<= 8;
1077
- }
1129
+ numBits_ = numBits > 64 ? numBits - 64 : 0;
1078
1130
 
1079
- if (i < len_) {
1080
- cell_ |= bytes8(strBytes_[i]) >> 56;
1081
- }
1131
+ result.high = randBoundedBits64(numBits_);
1132
+
1133
+ return result;
1134
+ }
1082
1135
 
1083
- if (i % 8 == 7) {
1084
- result_.value[i / 8] = setPublic64(uint64(cell_));
1085
- }
1086
- }
1136
+ function add(gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1137
+ gtUint128 memory result;
1138
+
1139
+ // Add low parts
1140
+ result.low = add(a.low, b.low);
1141
+
1142
+ // Check if there's a carry from low addition
1143
+ gtBool carry = lt(result.low, a.low);
1144
+
1145
+ // Add high parts with carry if needed
1146
+ result.high = add(a.high, b.high);
1147
+
1148
+ // Add carry to high part if needed
1149
+ result.high = mux(carry, result.high, add(result.high, uint64(1)));
1150
+
1151
+ return result;
1152
+ }
1087
1153
 
1088
- return result_;
1154
+ function checkedAdd(gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1155
+ gtUint128 memory result;
1156
+
1157
+ // Add low parts
1158
+ result.low = add(a.low, b.low);
1159
+
1160
+ // Check if there's a carry from low addition
1161
+ gtBool carry = lt(result.low, a.low);
1162
+
1163
+ // Add high parts with carry if needed
1164
+ result.high = checkedAdd(a.high, b.high);
1165
+
1166
+ // Add carry to high part if needed
1167
+ result.high = mux(carry, result.high, checkedAdd(result.high, uint64(1)));
1168
+
1169
+ return result;
1089
1170
  }
1090
1171
 
1091
- function decrypt(gtString memory ct) internal returns (string memory){
1092
- uint256 len_ = ct.value.length;
1093
- bytes memory result_ = new bytes(len_ * 8);
1172
+ function checkedAddWithOverflowBit(gtUint128 memory a, gtUint128 memory b) internal returns (gtBool, gtUint128 memory) {
1173
+ gtBool bit = setPublic(false);
1174
+ gtUint128 memory result;
1175
+
1176
+ // Add low parts
1177
+ result.low = add(a.low, b.low);
1178
+
1179
+ // Check if there's a carry from low addition
1180
+ gtBool carry = lt(result.low, a.low);
1181
+
1182
+ // Add high parts with carry if needed
1183
+ (gtBool overflow, gtUint64 high) = checkedAddWithOverflowBit(a.high, b.high);
1184
+ (gtBool overflowWithCarry, gtUint64 highWithCarry) = checkedAddWithOverflowBit(high, uint64(1));
1094
1185
 
1095
- bytes8 temp_;
1186
+ // Handle carry if needed
1187
+ bit = mux(carry, overflow, or(overflow, overflowWithCarry));
1188
+ result.high = mux(carry, high, highWithCarry);
1189
+
1190
+ return (bit, result);
1191
+ }
1096
1192
 
1097
- uint256 resultIndex;
1193
+ function sub(gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1194
+ gtUint128 memory result;
1098
1195
 
1099
- for (uint256 i = 0; i < len_; ++i) {
1100
- temp_ = bytes8(decrypt(ct.value[i]));
1196
+ // Subtract low parts
1197
+ result.low = sub(a.low, b.low);
1198
+
1199
+ // Check if there's a borrow from low subtraction
1200
+ gtBool borrow = lt(a.low, b.low);
1201
+
1202
+ // Subtract high parts with borrow if needed
1203
+ result.high = sub(a.high, b.high);
1204
+
1205
+ // Subtract borrow from high part if needed
1206
+ result.high = mux(borrow, result.high, sub(result.high, uint64(1)));
1207
+
1208
+ return result;
1209
+ }
1101
1210
 
1102
- assembly {
1103
- // Copy the bytes directly into the result array using assembly.
1104
- mstore(add(result_, add(0x20, resultIndex)), temp_)
1105
- }
1211
+ function checkedSub(gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1212
+ gtUint128 memory result;
1213
+
1214
+ // Subtract low parts
1215
+ result.low = sub(a.low, b.low);
1216
+
1217
+ // Check if there's a borrow from low subtraction
1218
+ gtBool borrow = lt(a.low, b.low);
1219
+
1220
+ // Subtract high parts with borrow if needed
1221
+ result.high = checkedSub(a.high, b.high);
1222
+
1223
+ // Subtract borrow from high part if needed
1224
+ result.high = mux(
1225
+ borrow,
1226
+ result.high,
1227
+ checkedSub(
1228
+ mux(borrow, add(result.high, uint64(1)), result.high),
1229
+ uint64(1)
1230
+ )
1231
+ );
1232
+
1233
+ return result;
1234
+ }
1106
1235
 
1107
- resultIndex += 8;
1108
- }
1236
+ function checkedSubWithOverflowBit(gtUint128 memory a, gtUint128 memory b) internal returns (gtBool, gtUint128 memory) {
1237
+ gtBool bit = setPublic(false);
1238
+ gtUint128 memory result;
1239
+
1240
+ // Subtract low parts
1241
+ result.low = sub(a.low, b.low);
1242
+
1243
+ // Check if there's a borrow from low subtraction
1244
+ gtBool borrow = lt(a.low, b.low);
1245
+
1246
+ // Subtract high parts with borrow if needed
1247
+ (gtBool overflow, gtUint64 high) = checkedSubWithOverflowBit(a.high, b.high);
1248
+ (gtBool overflowWithCarry, gtUint64 highWithCarry) = checkedSubWithOverflowBit(high, uint64(1));
1109
1249
 
1110
- return string(result_);
1250
+ // Handle borrow if needed
1251
+ bit = mux(borrow, overflow, or(overflow, overflowWithCarry));
1252
+ result.high = mux(borrow, high, highWithCarry);
1253
+
1254
+ return (bit, result);
1111
1255
  }
1112
1256
 
1113
- function eq(gtString memory a, gtString memory b) internal returns (gtBool) {
1114
- uint256 len = a.value.length;
1257
+ function _mul64(gtUint64 a, gtUint64 b) private returns (gtUint64, gtUint64) {
1258
+ uint64 MAX_UINT32 = uint64(type(uint32).max);
1115
1259
 
1116
- // note that we are not leaking information since the array length is visible to all
1117
- if (len != b.value.length) return setPublic(false);
1260
+ gtUint64 pp0;
1261
+ gtUint64 pp1;
1262
+ gtUint64 pp2;
1263
+ gtUint64 pp3;
1118
1264
 
1119
- gtBool result_ = eq(a.value[0], b.value[0]);
1265
+ {
1266
+ // Split the numbers into 32-bit parts
1267
+ gtUint64 aLow = and(a, MAX_UINT32);
1268
+ gtUint64 aHigh = shr(a, 32);
1269
+ gtUint64 bLow = and(b, MAX_UINT32);
1270
+ gtUint64 bHigh = shr(b, 32);
1120
1271
 
1121
- for (uint256 i = 1; i < len; ++i) {
1122
- result_ = and(result_, eq(a.value[i], b.value[i]));
1272
+ // Compute partial products
1273
+ pp0 = mul(aLow, bLow);
1274
+ pp1 = mul(aLow, bHigh);
1275
+ pp2 = mul(aHigh, bLow);
1276
+ pp3 = mul(aHigh, bHigh);
1123
1277
  }
1124
1278
 
1125
- return result_;
1279
+ // Compute high and low parts
1280
+ gtUint64 mid = add(
1281
+ add(
1282
+ shr(pp0, 32),
1283
+ and(pp1, MAX_UINT32)
1284
+ ),
1285
+ and(pp2, MAX_UINT32)
1286
+ );
1287
+ gtUint64 carry = shr(mid, 32);
1288
+
1289
+ gtUint64 high = add(
1290
+ add(pp3, shr(pp1, 32)),
1291
+ add(shr(pp2, 32), carry)
1292
+ );
1293
+ gtUint64 low = or(
1294
+ and(pp0, MAX_UINT32),
1295
+ shl(and(mid, MAX_UINT32), 32)
1296
+ );
1297
+
1298
+ return (high, low);
1299
+ }
1300
+
1301
+ function mul(gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1302
+ gtUint128 memory result;
1303
+
1304
+ // Compute partial products
1305
+ (gtUint64 pp0, gtUint64 low) = _mul64(a.low, b.low);
1306
+ (, gtUint64 pp1) = _mul64(a.high, b.low);
1307
+ (, gtUint64 pp2) = _mul64(a.low, b.high);
1308
+
1309
+ // Compute the high and low parts
1310
+ result.high = add(
1311
+ add(pp0, pp1),
1312
+ pp2
1313
+ );
1314
+ result.low = low;
1315
+
1316
+ return result;
1317
+ }
1318
+
1319
+ function checkedMul(gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1320
+ gtUint128 memory result;
1321
+
1322
+ // Compute partial products
1323
+ (gtUint64 pp0, gtUint64 low) = _mul64(a.low, b.low);
1324
+ (gtUint64 pp2, gtUint64 pp1) = _mul64(a.high, b.low);
1325
+ (gtUint64 pp4, gtUint64 pp3) = _mul64(a.low, b.high);
1326
+ (gtUint64 pp6, gtUint64 pp5) = _mul64(a.high, b.high);
1327
+
1328
+ // Compute the high and low parts
1329
+ result.high = checkedAdd(
1330
+ checkedAdd(pp0, pp1),
1331
+ pp3
1332
+ );
1333
+ result.low = low;
1334
+
1335
+ // Check for overflow
1336
+ checkedSub(
1337
+ uint64(0),
1338
+ add(
1339
+ add(pp2, pp4),
1340
+ add(pp5, pp6)
1341
+ )
1342
+ );
1343
+
1344
+ return result;
1126
1345
  }
1127
1346
 
1128
- function ne(gtString memory a, gtString memory b) internal returns (gtBool) {
1129
- uint256 len = a.value.length;
1347
+ function checkedMulWithOverflowBit(gtUint128 memory a, gtUint128 memory b) internal returns (gtBool, gtUint128 memory) {
1348
+ gtUint128 memory result;
1130
1349
 
1131
- // note that we are not leaking information since the array length is visible to all
1132
- if (len != b.value.length) return setPublic(true);
1350
+ // Compute partial products
1351
+ (gtUint64 pp0, gtUint64 low) = _mul64(a.low, b.low);
1352
+ (gtUint64 pp2, gtUint64 pp1) = _mul64(a.high, b.low);
1353
+ (gtUint64 pp4, gtUint64 pp3) = _mul64(a.low, b.high);
1354
+ (gtUint64 pp6, gtUint64 pp5) = _mul64(a.high, b.high);
1133
1355
 
1134
- gtBool result_ = ne(a.value[0], b.value[0]);
1356
+ // Compute the high and low parts
1357
+ gtBool overflow1;
1135
1358
 
1136
- for (uint256 i = 1; i < len; ++i) {
1137
- result_ = or(result_, ne(a.value[i], b.value[i]));
1138
- }
1359
+ (gtBool overflow0, gtUint64 high) = checkedAddWithOverflowBit(pp0, pp1);
1360
+ (overflow1, result.high) = checkedAddWithOverflowBit(high, pp3);
1139
1361
 
1140
- return result_;
1141
- }
1362
+ result.low = low;
1142
1363
 
1364
+ // Check for overflow
1365
+ gtBool bit = or(
1366
+ or(overflow0, overflow1),
1367
+ gt(
1368
+ add(
1369
+ add(pp2, pp4),
1370
+ add(pp5, pp6)
1371
+ ),
1372
+ uint64(0)
1373
+ )
1374
+ );
1375
+
1376
+ return (bit, result);
1377
+ }
1143
1378
 
1144
- // =========== Operations with LHS_PUBLIC parameter ===========
1145
- // =========== 8 bit operations ==============
1379
+ function and(gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1380
+ gtUint128 memory result;
1146
1381
 
1147
- function add(uint8 a, gtUint8 b) internal returns (gtUint8) {
1148
- return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1149
- Add(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1382
+ result.low = and(a.low, b.low);
1383
+ result.high = and(a.high, b.high);
1384
+
1385
+ return result;
1150
1386
  }
1151
1387
 
1152
- function checkedAdd(uint8 a, gtUint8 b) internal returns (gtUint8) {
1153
- (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
1154
- CheckedAdd(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
1388
+ function or(gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1389
+ gtUint128 memory result;
1155
1390
 
1156
- return checkRes8(gtBool.wrap(bit), gtUint8.wrap(res));
1391
+ result.low = or(a.low, b.low);
1392
+ result.high = or(a.high, b.high);
1393
+
1394
+ return result;
1157
1395
  }
1158
1396
 
1159
- function checkedAddWithOverflowBit(uint8 a, gtUint8 b) internal returns (gtBool, gtUint8) {
1160
- (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
1161
- CheckedAdd(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
1397
+ function xor(gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1398
+ gtUint128 memory result;
1162
1399
 
1163
- return (gtBool.wrap(bit), gtUint8.wrap(res));
1400
+ result.low = xor(a.low, b.low);
1401
+ result.high = xor(a.high, b.high);
1402
+
1403
+ return result;
1164
1404
  }
1165
1405
 
1166
- function sub(uint8 a, gtUint8 b) internal returns (gtUint8) {
1167
- return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1168
- Sub(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1406
+ function eq(gtUint128 memory a, gtUint128 memory b) internal returns (gtBool) {
1407
+ return and(eq(a.low, b.low), eq(a.high, b.high));
1169
1408
  }
1170
1409
 
1171
- function checkedSub(uint8 a, gtUint8 b) internal returns (gtUint8) {
1172
- (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
1173
- CheckedSub(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
1174
-
1175
- return checkRes8(gtBool.wrap(bit), gtUint8.wrap(res));
1410
+ function ne(gtUint128 memory a, gtUint128 memory b) internal returns (gtBool) {
1411
+ return or(ne(a.low, b.low), ne(a.high, b.high));
1176
1412
  }
1177
1413
 
1178
- function checkedSubWithOverflowBit(uint8 a, gtUint8 b) internal returns (gtBool, gtUint8) {
1179
- (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
1180
- CheckedSub(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
1414
+ function ge(gtUint128 memory a, gtUint128 memory b) internal returns (gtBool) {
1415
+ gtBool highEqual = eq(a.high, b.high);
1181
1416
 
1182
- return (gtBool.wrap(bit), gtUint8.wrap(res));
1417
+ return mux(highEqual, gt(a.high, b.high), ge(a.low, b.low));
1183
1418
  }
1184
1419
 
1185
- function mul(uint8 a, gtUint8 b) internal returns (gtUint8) {
1186
- return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1187
- Mul(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1420
+ function gt(gtUint128 memory a, gtUint128 memory b) internal returns (gtBool) {
1421
+ gtBool highEqual = eq(a.high, b.high);
1422
+
1423
+ return mux(highEqual, gt(a.high, b.high), gt(a.low, b.low));
1188
1424
  }
1189
1425
 
1190
- function checkedMul(uint8 a, gtUint8 b) internal returns (gtUint8) {
1191
- (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
1192
- CheckedMul(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
1426
+ function le(gtUint128 memory a, gtUint128 memory b) internal returns (gtBool) {
1427
+ gtBool highEqual = eq(a.high, b.high);
1193
1428
 
1194
- return checkRes8(gtBool.wrap(bit), gtUint8.wrap(res));
1429
+ return mux(highEqual, lt(a.high, b.high), le(a.low, b.low));
1195
1430
  }
1196
1431
 
1197
- function checkedMulWithOverflowBit(uint8 a, gtUint8 b) internal returns (gtBool, gtUint8) {
1198
- (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
1199
- CheckedMul(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
1432
+ function lt(gtUint128 memory a, gtUint128 memory b) internal returns (gtBool) {
1433
+ gtBool highEqual = eq(a.high, b.high);
1200
1434
 
1201
- return (gtBool.wrap(bit), gtUint8.wrap(res));
1435
+ return mux(highEqual, lt(a.high, b.high), lt(a.low, b.low));
1202
1436
  }
1203
1437
 
1204
- function div(uint8 a, gtUint8 b) internal returns (gtUint8) {
1205
- return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1206
- Div(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1207
- }
1438
+ function min(gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1439
+ gtBool highEqual = eq(a.high, b.high);
1440
+ gtBool aHighLessThan = lt(a.high, b.high);
1441
+ gtBool aLowLessThan = lt(a.low, b.low);
1208
1442
 
1209
- function rem(uint8 a, gtUint8 b) internal returns (gtUint8) {
1210
- return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1211
- Rem(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1443
+ return mux(
1444
+ highEqual,
1445
+ mux(aHighLessThan, b, a),
1446
+ mux(aLowLessThan, b, a)
1447
+ );
1212
1448
  }
1213
1449
 
1214
- function and(uint8 a, gtUint8 b) internal returns (gtUint8) {
1215
- return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1216
- And(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1217
- }
1450
+ function max(gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1451
+ gtBool highEqual = eq(a.high, b.high);
1452
+ gtBool aHighGreaterThan = gt(a.high, b.high);
1453
+ gtBool aLowGreaterThan = gt(a.low, b.low);
1218
1454
 
1219
- function or(uint8 a, gtUint8 b) internal returns (gtUint8) {
1220
- return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1221
- Or(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1455
+ return mux(
1456
+ highEqual,
1457
+ mux(aHighGreaterThan, b, a),
1458
+ mux(aLowGreaterThan, b, a)
1459
+ );
1222
1460
  }
1223
1461
 
1224
- function xor(uint8 a, gtUint8 b) internal returns (gtUint8) {
1225
- return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1226
- Xor(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1462
+ function decrypt(gtUint128 memory ct) internal returns (uint128) {
1463
+ uint64 highPart = decrypt(ct.high);
1464
+ uint64 lowPart = decrypt(ct.low);
1465
+
1466
+ // Combine high and low parts
1467
+ return uint128(highPart) << 64 | uint128(lowPart);
1227
1468
  }
1228
1469
 
1229
- function eq(uint8 a, gtUint8 b) internal returns (gtBool) {
1230
- return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1231
- Eq(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1232
- }
1470
+ function mux(gtBool bit, gtUint128 memory a, gtUint128 memory b) internal returns (gtUint128 memory) {
1471
+ gtUint128 memory result;
1233
1472
 
1234
- function ne(uint8 a, gtUint8 b) internal returns (gtBool) {
1235
- return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1236
- Ne(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1473
+ result.low = mux(bit, a.low, b.low);
1474
+ result.high = mux(bit, a.high, b.high);
1475
+
1476
+ return result;
1237
1477
  }
1238
1478
 
1239
- function ge(uint8 a, gtUint8 b) internal returns (gtBool) {
1240
- return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1241
- Ge(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1242
- }
1479
+ function transfer(gtUint128 memory a, gtUint128 memory b, gtUint128 memory amount) internal returns (gtUint128 memory, gtUint128 memory, gtBool) {
1480
+ gtBool success = MpcCore.ge(a, amount);
1243
1481
 
1244
- function gt(uint8 a, gtUint8 b) internal returns (gtBool) {
1245
- return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1246
- Gt(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1482
+ gtUint128 memory a_ = MpcCore.mux(success, a, MpcCore.sub(a, amount));
1483
+ gtUint128 memory b_ = MpcCore.mux(success, b, MpcCore.add(b, amount));
1484
+
1485
+ return (a_, b_, success);
1247
1486
  }
1248
1487
 
1249
- function le(uint8 a, gtUint8 b) internal returns (gtBool) {
1250
- return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1251
- Le(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1252
- }
1488
+ function transferWithAllowance(gtUint128 memory a, gtUint128 memory b, gtUint128 memory amount, gtUint128 memory allowance) internal returns (gtUint128 memory, gtUint128 memory, gtBool, gtUint128 memory) {
1489
+ gtBool success = MpcCore.and(MpcCore.ge(a, amount), MpcCore.le(amount, allowance));
1253
1490
 
1254
- function lt(uint8 a, gtUint8 b) internal returns (gtBool) {
1255
- return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1256
- Lt(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1491
+ gtUint128 memory a_ = MpcCore.mux(success, a, MpcCore.sub(a, amount));
1492
+ gtUint128 memory b_ = MpcCore.mux(success, b, MpcCore.add(b, amount));
1493
+ gtUint128 memory allowance_ = MpcCore.mux(success, allowance, MpcCore.sub(allowance, amount));
1494
+
1495
+ return (a_, b_, success, allowance_);
1257
1496
  }
1258
1497
 
1259
- function min(uint8 a, gtUint8 b) internal returns (gtUint8) {
1260
- return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1261
- Min(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1262
- }
1498
+ // =========== 256 bit operations ============
1263
1499
 
1264
- function max(uint8 a, gtUint8 b) internal returns (gtUint8) {
1265
- return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1266
- Max(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
1500
+ function _splitUint256(uint256 number) private returns (uint128, uint128) {
1501
+ return (uint128(number >> 128), uint128(number));
1267
1502
  }
1268
1503
 
1269
- function mux(gtBool bit, uint8 a, gtUint8 b) internal returns (gtUint8){
1270
- return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1271
- Mux(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), gtBool.unwrap(bit), uint256(a), gtUint8.unwrap(b)));
1504
+ function validateCiphertext(itUint256 memory input) internal returns (gtUint256 memory) {
1505
+ gtUint256 memory result;
1506
+
1507
+ itUint128 memory highInput;
1508
+ highInput.ciphertext = input.ciphertext.high;
1509
+ highInput.signature = input.signature[0];
1510
+
1511
+ itUint128 memory lowInput;
1512
+ lowInput.ciphertext = input.ciphertext.low;
1513
+ lowInput.signature = input.signature[1];
1514
+
1515
+ result.high = validateCiphertext(highInput);
1516
+ result.low = validateCiphertext(lowInput);
1517
+
1518
+ return result;
1272
1519
  }
1273
1520
 
1521
+ function onBoard(ctUint256 memory ct) internal returns (gtUint256 memory) {
1522
+ gtUint256 memory result;
1274
1523
 
1275
- // =========== 16 bit operations ==============
1524
+ result.high = onBoard(ct.high);
1525
+ result.low = onBoard(ct.low);
1276
1526
 
1277
- function add(uint16 a, gtUint16 b) internal returns (gtUint16) {
1278
- return gtUint16.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
1279
- Add(combineEnumsToBytes3(MPC_TYPE.SUINT16_T, MPC_TYPE.SUINT16_T, ARGS.LHS_PUBLIC), uint256(a), gtUint16.unwrap(b)));
1527
+ return result;
1280
1528
  }
1281
1529
 
1282
- function checkedAdd(uint16 a, gtUint16 b) internal returns (gtUint16) {
1530
+ function offBoard(gtUint256 memory pt) internal returns (ctUint256 memory) {
1531
+ ctUint256 memory result;
1532
+
1533
+ result.high = offBoard(pt.high);
1534
+ result.low = offBoard(pt.low);
1535
+
1536
+ return result;
1537
+ }
1538
+
1539
+ function offBoardToUser(gtUint256 memory pt, address addr) internal returns (ctUint256 memory) {
1540
+ ctUint256 memory result;
1541
+
1542
+ result.high = offBoardToUser(pt.high, addr);
1543
+ result.low = offBoardToUser(pt.low, addr);
1544
+
1545
+ return result;
1546
+ }
1547
+
1548
+ function offBoardCombined(gtUint256 memory pt, address addr) internal returns (utUint256 memory) {
1549
+ utUint256 memory result;
1550
+
1551
+ result.ciphertext = offBoard(pt);
1552
+ result.userCiphertext = offBoardToUser(pt, addr);
1553
+
1554
+ return result;
1555
+ }
1556
+
1557
+ function setPublic256(uint256 pt) internal returns (gtUint256 memory) {
1558
+ gtUint256 memory result;
1559
+
1560
+ // Split the 256-bit value into high and low 128-bit parts
1561
+ uint128 low = uint128(pt);
1562
+ uint128 high = uint128(pt >> 128);
1563
+
1564
+ result.high = setPublic128(high);
1565
+ result.low = setPublic128(low);
1566
+
1567
+ return result;
1568
+ }
1569
+
1570
+ function rand256() internal returns (gtUint256 memory) {
1571
+ gtUint256 memory result;
1572
+
1573
+ result.high = rand128();
1574
+ result.low = rand128();
1575
+
1576
+ return result;
1577
+ }
1578
+
1579
+ function randBoundedBits256(uint8 numBits) internal returns (gtUint256 memory) {
1580
+ gtUint256 memory result;
1581
+
1582
+ uint8 numBits_ = numBits > 128 ? 128 : numBits;
1583
+
1584
+ result.low = randBoundedBits128(numBits_);
1585
+
1586
+ numBits_ = numBits > 128 ? numBits - 128 : 0;
1587
+
1588
+ result.high = randBoundedBits128(numBits_);
1589
+
1590
+ return result;
1591
+ }
1592
+
1593
+ function add(gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1594
+ gtUint256 memory result;
1595
+
1596
+ // Add low parts
1597
+ result.low = add(a.low, b.low);
1598
+
1599
+ // Check if there's a carry from low addition
1600
+ gtBool carry = lt(result.low, a.low);
1601
+
1602
+ // Add high parts with carry if needed
1603
+ result.high = add(a.high, b.high);
1604
+
1605
+ // Add carry to high part if needed
1606
+ result.high = mux(carry, result.high, add(result.high, uint128(1)));
1607
+
1608
+ return result;
1609
+ }
1610
+
1611
+ function checkedAdd(gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1612
+ gtUint256 memory result;
1613
+
1614
+ // Add low parts
1615
+ result.low = add(a.low, b.low);
1616
+
1617
+ // Check if there's a carry from low addition
1618
+ gtBool carry = lt(result.low, a.low);
1619
+
1620
+ // Add high parts with carry if needed
1621
+ result.high = checkedAdd(a.high, b.high);
1622
+
1623
+ // Add carry to high part if needed
1624
+ result.high = mux(carry, result.high, checkedAdd(result.high, uint128(1)));
1625
+
1626
+ return result;
1627
+ }
1628
+
1629
+ function checkedAddWithOverflowBit(gtUint256 memory a, gtUint256 memory b) internal returns (gtBool, gtUint256 memory) {
1630
+ gtBool bit = setPublic(false);
1631
+ gtUint256 memory result;
1632
+
1633
+ // Add low parts
1634
+ result.low = add(a.low, b.low);
1635
+
1636
+ // Check if there's a carry from low addition
1637
+ gtBool carry = lt(result.low, a.low);
1638
+
1639
+ // Add high parts with carry if needed
1640
+ (gtBool overflow, gtUint128 memory high) = checkedAddWithOverflowBit(a.high, b.high);
1641
+ (gtBool overflowWithCarry, gtUint128 memory highWithCarry) = checkedAddWithOverflowBit(high, uint128(1));
1642
+
1643
+ // Handle carry if needed
1644
+ bit = mux(carry, overflow, or(overflow, overflowWithCarry));
1645
+ result.high = mux(carry, high, highWithCarry);
1646
+
1647
+ return (bit, result);
1648
+ }
1649
+
1650
+ function sub(gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1651
+ gtUint256 memory result;
1652
+
1653
+ // Subtract low parts
1654
+ result.low = sub(a.low, b.low);
1655
+
1656
+ // Check if there's a borrow from low subtraction
1657
+ gtBool borrow = lt(a.low, b.low);
1658
+
1659
+ // Subtract high parts with borrow if needed
1660
+ result.high = sub(a.high, b.high);
1661
+
1662
+ // Subtract borrow from high part if needed
1663
+ result.high = mux(borrow, result.high, sub(result.high, uint128(1)));
1664
+
1665
+ return result;
1666
+ }
1667
+
1668
+ function checkedSub(gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1669
+ gtUint256 memory result;
1670
+
1671
+ // Subtract low parts
1672
+ result.low = sub(a.low, b.low);
1673
+
1674
+ // Check if there's a borrow from low subtraction
1675
+ gtBool borrow = lt(a.low, b.low);
1676
+
1677
+ // Subtract high parts with borrow if needed
1678
+ result.high = checkedSub(a.high, b.high);
1679
+
1680
+ // Subtract borrow from high part if needed
1681
+ result.high = mux(
1682
+ borrow,
1683
+ result.high,
1684
+ checkedSub(
1685
+ mux(borrow, add(result.high, uint128(1)), result.high),
1686
+ uint128(1)
1687
+ )
1688
+ );
1689
+
1690
+ return result;
1691
+ }
1692
+
1693
+ function checkedSubWithOverflowBit(gtUint256 memory a, gtUint256 memory b) internal returns (gtBool, gtUint256 memory) {
1694
+ gtBool bit = setPublic(false);
1695
+ gtUint256 memory result;
1696
+
1697
+ // Subtract low parts
1698
+ result.low = sub(a.low, b.low);
1699
+
1700
+ // Check if there's a borrow from low subtraction
1701
+ gtBool borrow = lt(a.low, b.low);
1702
+
1703
+ // Subtract high parts with borrow if needed
1704
+ (gtBool overflow, gtUint128 memory high) = checkedSubWithOverflowBit(a.high, b.high);
1705
+ (gtBool overflowWithCarry, gtUint128 memory highWithCarry) = checkedSubWithOverflowBit(high, uint128(1));
1706
+
1707
+ // Handle borrow if needed
1708
+ bit = mux(borrow, overflow, or(overflow, overflowWithCarry));
1709
+ result.high = mux(borrow, high, highWithCarry);
1710
+
1711
+ return (bit, result);
1712
+ }
1713
+
1714
+ function _mul128(gtUint128 memory a, gtUint128 memory b) private returns (gtUint128 memory, gtUint128 memory) {
1715
+ uint128 MAX_UINT64 = uint128(type(uint64).max);
1716
+
1717
+ gtUint128 memory pp0;
1718
+ gtUint128 memory pp1;
1719
+ gtUint128 memory pp2;
1720
+ gtUint128 memory pp3;
1721
+
1722
+ {
1723
+ // Split the numbers into 32-bit parts
1724
+ gtUint128 memory aLow = and(a, MAX_UINT64);
1725
+ gtUint128 memory aHigh = shr(a, 64);
1726
+ gtUint128 memory bLow = and(b, MAX_UINT64);
1727
+ gtUint128 memory bHigh = shr(b, 64);
1728
+
1729
+ // Compute partial products
1730
+ pp0 = mul(aLow, bLow);
1731
+ pp1 = mul(aLow, bHigh);
1732
+ pp2 = mul(aHigh, bLow);
1733
+ pp3 = mul(aHigh, bHigh);
1734
+ }
1735
+
1736
+ // Compute high and low parts
1737
+ gtUint128 memory mid = add(
1738
+ add(
1739
+ shr(pp0, 64),
1740
+ and(pp1, MAX_UINT64)
1741
+ ),
1742
+ and(pp2, MAX_UINT64)
1743
+ );
1744
+ gtUint128 memory carry = shr(mid, 64);
1745
+
1746
+ gtUint128 memory high = add(
1747
+ add(pp3, shr(pp1, 64)),
1748
+ add(shr(pp2, 64), carry)
1749
+ );
1750
+ gtUint128 memory low = or(
1751
+ and(pp0, MAX_UINT64),
1752
+ shl(and(mid, MAX_UINT64), 64)
1753
+ );
1754
+
1755
+ return (high, low);
1756
+ }
1757
+
1758
+ function mul(gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1759
+ gtUint256 memory result;
1760
+
1761
+ // Compute partial products
1762
+ (gtUint128 memory pp0, gtUint128 memory low) = _mul128(a.low, b.low);
1763
+ (, gtUint128 memory pp1) = _mul128(a.high, b.low);
1764
+ (, gtUint128 memory pp2) = _mul128(a.low, b.high);
1765
+
1766
+ // Compute the high and low parts
1767
+ result.high = add(
1768
+ add(pp0, pp1),
1769
+ pp2
1770
+ );
1771
+ result.low = low;
1772
+
1773
+ return result;
1774
+ }
1775
+
1776
+ function checkedMul(gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1777
+ gtUint256 memory result;
1778
+
1779
+ // Compute partial products
1780
+ (gtUint128 memory pp0, gtUint128 memory low) = _mul128(a.low, b.low);
1781
+ (gtUint128 memory pp2, gtUint128 memory pp1) = _mul128(a.high, b.low);
1782
+ (gtUint128 memory pp4, gtUint128 memory pp3) = _mul128(a.low, b.high);
1783
+ (gtUint128 memory pp6, gtUint128 memory pp5) = _mul128(a.high, b.high);
1784
+
1785
+ // Compute the high and low parts
1786
+ result.high = checkedAdd(
1787
+ checkedAdd(pp0, pp1),
1788
+ pp3
1789
+ );
1790
+ result.low = low;
1791
+
1792
+ // Check for overflow
1793
+ checkedSub(
1794
+ uint128(0),
1795
+ add(
1796
+ add(pp2, pp4),
1797
+ add(pp5, pp6)
1798
+ )
1799
+ );
1800
+
1801
+ return result;
1802
+ }
1803
+
1804
+ function checkedMulWithOverflowBit(gtUint256 memory a, gtUint256 memory b) internal returns (gtBool, gtUint256 memory) {
1805
+ gtUint256 memory result;
1806
+
1807
+ // Compute partial products
1808
+ (gtUint128 memory pp0, gtUint128 memory low) = _mul128(a.low, b.low);
1809
+ (gtUint128 memory pp2, gtUint128 memory pp1) = _mul128(a.high, b.low);
1810
+ (gtUint128 memory pp4, gtUint128 memory pp3) = _mul128(a.low, b.high);
1811
+ (gtUint128 memory pp6, gtUint128 memory pp5) = _mul128(a.high, b.high);
1812
+
1813
+ // Compute the high and low parts
1814
+ gtBool overflow1;
1815
+
1816
+ (gtBool overflow0, gtUint128 memory high) = checkedAddWithOverflowBit(pp0, pp1);
1817
+ (overflow1, result.high) = checkedAddWithOverflowBit(high, pp3);
1818
+
1819
+ result.low = low;
1820
+
1821
+ // Check for overflow
1822
+ gtBool bit = or(
1823
+ or(overflow0, overflow1),
1824
+ gt(
1825
+ add(
1826
+ add(pp2, pp4),
1827
+ add(pp5, pp6)
1828
+ ),
1829
+ uint128(0)
1830
+ )
1831
+ );
1832
+
1833
+ return (bit, result);
1834
+ }
1835
+
1836
+ function and(gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1837
+ gtUint256 memory result;
1838
+
1839
+ result.low = and(a.low, b.low);
1840
+ result.high = and(a.high, b.high);
1841
+
1842
+ return result;
1843
+ }
1844
+
1845
+ function or(gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1846
+ gtUint256 memory result;
1847
+
1848
+ result.low = or(a.low, b.low);
1849
+ result.high = or(a.high, b.high);
1850
+
1851
+ return result;
1852
+ }
1853
+
1854
+ function xor(gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1855
+ gtUint256 memory result;
1856
+
1857
+ result.low = xor(a.low, b.low);
1858
+ result.high = xor(a.high, b.high);
1859
+
1860
+ return result;
1861
+ }
1862
+
1863
+ function eq(gtUint256 memory a, gtUint256 memory b) internal returns (gtBool) {
1864
+ return and(eq(a.low, b.low), eq(a.high, b.high));
1865
+ }
1866
+
1867
+ function ne(gtUint256 memory a, gtUint256 memory b) internal returns (gtBool) {
1868
+ return or(ne(a.low, b.low), ne(a.high, b.high));
1869
+ }
1870
+
1871
+ function ge(gtUint256 memory a, gtUint256 memory b) internal returns (gtBool) {
1872
+ gtBool highEqual = eq(a.high, b.high);
1873
+
1874
+ return mux(highEqual, gt(a.high, b.high), ge(a.low, b.low));
1875
+ }
1876
+
1877
+ function gt(gtUint256 memory a, gtUint256 memory b) internal returns (gtBool) {
1878
+ gtBool highEqual = eq(a.high, b.high);
1879
+
1880
+ return mux(highEqual, gt(a.high, b.high), gt(a.low, b.low));
1881
+ }
1882
+
1883
+ function le(gtUint256 memory a, gtUint256 memory b) internal returns (gtBool) {
1884
+ gtBool highEqual = eq(a.high, b.high);
1885
+
1886
+ return mux(highEqual, lt(a.high, b.high), le(a.low, b.low));
1887
+ }
1888
+
1889
+ function lt(gtUint256 memory a, gtUint256 memory b) internal returns (gtBool) {
1890
+ gtBool highEqual = eq(a.high, b.high);
1891
+
1892
+ return mux(highEqual, lt(a.high, b.high), lt(a.low, b.low));
1893
+ }
1894
+
1895
+ function min(gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1896
+ gtBool highEqual = eq(a.high, b.high);
1897
+ gtBool aHighLessThan = lt(a.high, b.high);
1898
+ gtBool aLowLessThan = lt(a.low, b.low);
1899
+
1900
+ return mux(
1901
+ highEqual,
1902
+ mux(aHighLessThan, b, a),
1903
+ mux(aLowLessThan, b, a)
1904
+ );
1905
+ }
1906
+
1907
+ function max(gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1908
+ gtBool highEqual = eq(a.high, b.high);
1909
+ gtBool aHighGreaterThan = gt(a.high, b.high);
1910
+ gtBool aLowGreaterThan = gt(a.low, b.low);
1911
+
1912
+ return mux(
1913
+ highEqual,
1914
+ mux(aHighGreaterThan, b, a),
1915
+ mux(aLowGreaterThan, b, a)
1916
+ );
1917
+ }
1918
+
1919
+ function decrypt(gtUint256 memory ct) internal returns (uint256) {
1920
+ uint128 highPart = decrypt(ct.high);
1921
+ uint128 lowPart = decrypt(ct.low);
1922
+
1923
+ // Combine high and low parts
1924
+ return uint256(highPart) << 128 | uint256(lowPart);
1925
+ }
1926
+
1927
+ function mux(gtBool bit, gtUint256 memory a, gtUint256 memory b) internal returns (gtUint256 memory) {
1928
+ gtUint256 memory result;
1929
+
1930
+ result.low = mux(bit, a.low, b.low);
1931
+ result.high = mux(bit, a.high, b.high);
1932
+
1933
+ return result;
1934
+ }
1935
+
1936
+ function transfer(gtUint256 memory a, gtUint256 memory b, gtUint256 memory amount) internal returns (gtUint256 memory, gtUint256 memory, gtBool) {
1937
+ gtBool success = MpcCore.ge(a, amount);
1938
+
1939
+ gtUint256 memory a_ = MpcCore.mux(success, a, MpcCore.sub(a, amount));
1940
+ gtUint256 memory b_ = MpcCore.mux(success, b, MpcCore.add(b, amount));
1941
+
1942
+ return (a_, b_, success);
1943
+ }
1944
+
1945
+ function transferWithAllowance(gtUint256 memory a, gtUint256 memory b, gtUint256 memory amount, gtUint256 memory allowance) internal returns (gtUint256 memory, gtUint256 memory, gtBool, gtUint256 memory) {
1946
+ gtBool success = MpcCore.and(MpcCore.ge(a, amount), MpcCore.le(amount, allowance));
1947
+
1948
+ gtUint256 memory a_ = MpcCore.mux(success, a, MpcCore.sub(a, amount));
1949
+ gtUint256 memory b_ = MpcCore.mux(success, b, MpcCore.add(b, amount));
1950
+ gtUint256 memory allowance_ = MpcCore.mux(success, allowance, MpcCore.sub(allowance, amount));
1951
+
1952
+ return (a_, b_, success, allowance_);
1953
+ }
1954
+
1955
+ // =========== String operations ============
1956
+
1957
+ function validateCiphertext(itString memory input) internal returns (gtString memory) {
1958
+ uint256 len_ = input.signature.length;
1959
+
1960
+ require(input.ciphertext.value.length == len_, "MPC_CORE: INVALID_INPUT_TEXT");
1961
+
1962
+ gtString memory gt_ = gtString(new gtUint64[](len_));
1963
+
1964
+ itUint64 memory it_;
1965
+
1966
+ for (uint256 i = 0; i < len_; ++i) {
1967
+ it_.ciphertext = input.ciphertext.value[i];
1968
+ it_.signature = input.signature[i];
1969
+
1970
+ gt_.value[i] = validateCiphertext(it_);
1971
+ }
1972
+
1973
+ return gt_;
1974
+ }
1975
+
1976
+ function onBoard(ctString memory ct) internal returns (gtString memory) {
1977
+ uint256 len_ = ct.value.length;
1978
+
1979
+ gtString memory gt_ = gtString(new gtUint64[](len_));
1980
+
1981
+ for (uint256 i = 0; i < len_; ++i) {
1982
+ gt_.value[i] = onBoard(ct.value[i]);
1983
+ }
1984
+
1985
+ return gt_;
1986
+ }
1987
+
1988
+ function offBoard(gtString memory pt) internal returns (ctString memory) {
1989
+ uint256 len_ = pt.value.length;
1990
+
1991
+ ctString memory ct_ = ctString(new ctUint64[](len_));
1992
+
1993
+ for (uint256 i = 0; i < len_; ++i) {
1994
+ ct_.value[i] = offBoard(pt.value[i]);
1995
+ }
1996
+
1997
+ return ct_;
1998
+ }
1999
+
2000
+ function offBoardToUser(gtString memory pt, address addr) internal returns (ctString memory) {
2001
+ uint256 len_ = pt.value.length;
2002
+
2003
+ ctString memory ct_ = ctString(new ctUint64[](len_));
2004
+
2005
+ for (uint256 i = 0; i < len_; ++i) {
2006
+ ct_.value[i] = offBoardToUser(pt.value[i], addr);
2007
+ }
2008
+
2009
+ return ct_;
2010
+ }
2011
+
2012
+ function offBoardCombined(gtString memory pt, address addr) internal returns (utString memory ut) {
2013
+ ut.ciphertext = offBoard(pt);
2014
+ ut.userCiphertext = offBoardToUser(pt, addr);
2015
+ }
2016
+
2017
+ function setPublicString(string memory pt) internal returns (gtString memory) {
2018
+ bytes memory strBytes_ = bytes(pt);
2019
+ uint256 len_ = strBytes_.length;
2020
+ uint256 count_ = (len_ + 7) / 8; // Number of bytes8 elements needed
2021
+
2022
+ gtString memory result_ = gtString(new gtUint64[](count_));
2023
+
2024
+ bytes8 cell_;
2025
+
2026
+ for (uint256 i = 0; i < count_ * 8; ++i) {
2027
+ if (i % 8 == 0) {
2028
+ cell_ = bytes8(0);
2029
+ } else {
2030
+ cell_ <<= 8;
2031
+ }
2032
+
2033
+ if (i < len_) {
2034
+ cell_ |= bytes8(strBytes_[i]) >> 56;
2035
+ }
2036
+
2037
+ if (i % 8 == 7) {
2038
+ result_.value[i / 8] = setPublic64(uint64(cell_));
2039
+ }
2040
+ }
2041
+
2042
+ return result_;
2043
+ }
2044
+
2045
+ function decrypt(gtString memory ct) internal returns (string memory){
2046
+ uint256 len_ = ct.value.length;
2047
+ bytes memory result_ = new bytes(len_ * 8);
2048
+
2049
+ bytes8 temp_;
2050
+
2051
+ uint256 resultIndex;
2052
+
2053
+ for (uint256 i = 0; i < len_; ++i) {
2054
+ temp_ = bytes8(decrypt(ct.value[i]));
2055
+
2056
+ assembly {
2057
+ // Copy the bytes directly into the result array using assembly.
2058
+ mstore(add(result_, add(0x20, resultIndex)), temp_)
2059
+ }
2060
+
2061
+ resultIndex += 8;
2062
+ }
2063
+
2064
+ return string(result_);
2065
+ }
2066
+
2067
+ function eq(gtString memory a, gtString memory b) internal returns (gtBool) {
2068
+ uint256 len = a.value.length;
2069
+
2070
+ // note that we are not leaking information since the array length is visible to all
2071
+ if (len != b.value.length) return setPublic(false);
2072
+
2073
+ gtBool result_ = eq(a.value[0], b.value[0]);
2074
+
2075
+ for (uint256 i = 1; i < len; ++i) {
2076
+ result_ = and(result_, eq(a.value[i], b.value[i]));
2077
+ }
2078
+
2079
+ return result_;
2080
+ }
2081
+
2082
+ function ne(gtString memory a, gtString memory b) internal returns (gtBool) {
2083
+ uint256 len = a.value.length;
2084
+
2085
+ // note that we are not leaking information since the array length is visible to all
2086
+ if (len != b.value.length) return setPublic(true);
2087
+
2088
+ gtBool result_ = ne(a.value[0], b.value[0]);
2089
+
2090
+ for (uint256 i = 1; i < len; ++i) {
2091
+ result_ = or(result_, ne(a.value[i], b.value[i]));
2092
+ }
2093
+
2094
+ return result_;
2095
+ }
2096
+
2097
+
2098
+ // =========== Operations with LHS_PUBLIC parameter ===========
2099
+ // =========== 8 bit operations ==============
2100
+
2101
+ function add(uint8 a, gtUint8 b) internal returns (gtUint8) {
2102
+ return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2103
+ Add(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2104
+ }
2105
+
2106
+ function checkedAdd(uint8 a, gtUint8 b) internal returns (gtUint8) {
2107
+ (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
2108
+ CheckedAdd(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
2109
+
2110
+ return checkRes8(gtBool.wrap(bit), gtUint8.wrap(res));
2111
+ }
2112
+
2113
+ function checkedAddWithOverflowBit(uint8 a, gtUint8 b) internal returns (gtBool, gtUint8) {
2114
+ (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
2115
+ CheckedAdd(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
2116
+
2117
+ return (gtBool.wrap(bit), gtUint8.wrap(res));
2118
+ }
2119
+
2120
+ function sub(uint8 a, gtUint8 b) internal returns (gtUint8) {
2121
+ return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2122
+ Sub(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2123
+ }
2124
+
2125
+ function checkedSub(uint8 a, gtUint8 b) internal returns (gtUint8) {
2126
+ (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
2127
+ CheckedSub(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
2128
+
2129
+ return checkRes8(gtBool.wrap(bit), gtUint8.wrap(res));
2130
+ }
2131
+
2132
+ function checkedSubWithOverflowBit(uint8 a, gtUint8 b) internal returns (gtBool, gtUint8) {
2133
+ (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
2134
+ CheckedSub(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
2135
+
2136
+ return (gtBool.wrap(bit), gtUint8.wrap(res));
2137
+ }
2138
+
2139
+ function mul(uint8 a, gtUint8 b) internal returns (gtUint8) {
2140
+ return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2141
+ Mul(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2142
+ }
2143
+
2144
+ function checkedMul(uint8 a, gtUint8 b) internal returns (gtUint8) {
2145
+ (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
2146
+ CheckedMul(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
2147
+
2148
+ return checkRes8(gtBool.wrap(bit), gtUint8.wrap(res));
2149
+ }
2150
+
2151
+ function checkedMulWithOverflowBit(uint8 a, gtUint8 b) internal returns (gtBool, gtUint8) {
2152
+ (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
2153
+ CheckedMul(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b));
2154
+
2155
+ return (gtBool.wrap(bit), gtUint8.wrap(res));
2156
+ }
2157
+
2158
+ function div(uint8 a, gtUint8 b) internal returns (gtUint8) {
2159
+ return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2160
+ Div(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2161
+ }
2162
+
2163
+ function rem(uint8 a, gtUint8 b) internal returns (gtUint8) {
2164
+ return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2165
+ Rem(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2166
+ }
2167
+
2168
+ function and(uint8 a, gtUint8 b) internal returns (gtUint8) {
2169
+ return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2170
+ And(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2171
+ }
2172
+
2173
+ function or(uint8 a, gtUint8 b) internal returns (gtUint8) {
2174
+ return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2175
+ Or(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2176
+ }
2177
+
2178
+ function xor(uint8 a, gtUint8 b) internal returns (gtUint8) {
2179
+ return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2180
+ Xor(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2181
+ }
2182
+
2183
+ function eq(uint8 a, gtUint8 b) internal returns (gtBool) {
2184
+ return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2185
+ Eq(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2186
+ }
2187
+
2188
+ function ne(uint8 a, gtUint8 b) internal returns (gtBool) {
2189
+ return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2190
+ Ne(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2191
+ }
2192
+
2193
+ function ge(uint8 a, gtUint8 b) internal returns (gtBool) {
2194
+ return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2195
+ Ge(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2196
+ }
2197
+
2198
+ function gt(uint8 a, gtUint8 b) internal returns (gtBool) {
2199
+ return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2200
+ Gt(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2201
+ }
2202
+
2203
+ function le(uint8 a, gtUint8 b) internal returns (gtBool) {
2204
+ return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2205
+ Le(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2206
+ }
2207
+
2208
+ function lt(uint8 a, gtUint8 b) internal returns (gtBool) {
2209
+ return gtBool.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2210
+ Lt(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2211
+ }
2212
+
2213
+ function min(uint8 a, gtUint8 b) internal returns (gtUint8) {
2214
+ return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2215
+ Min(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2216
+ }
2217
+
2218
+ function max(uint8 a, gtUint8 b) internal returns (gtUint8) {
2219
+ return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2220
+ Max(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), uint256(a), gtUint8.unwrap(b)));
2221
+ }
2222
+
2223
+ function mux(gtBool bit, uint8 a, gtUint8 b) internal returns (gtUint8){
2224
+ return gtUint8.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2225
+ Mux(combineEnumsToBytes3(MPC_TYPE.SUINT8_T, MPC_TYPE.SUINT8_T, ARGS.LHS_PUBLIC), gtBool.unwrap(bit), uint256(a), gtUint8.unwrap(b)));
2226
+ }
2227
+
2228
+
2229
+ // =========== 16 bit operations ==============
2230
+
2231
+ function add(uint16 a, gtUint16 b) internal returns (gtUint16) {
2232
+ return gtUint16.wrap(ExtendedOperations(address(MPC_PRECOMPILE)).
2233
+ Add(combineEnumsToBytes3(MPC_TYPE.SUINT16_T, MPC_TYPE.SUINT16_T, ARGS.LHS_PUBLIC), uint256(a), gtUint16.unwrap(b)));
2234
+ }
2235
+
2236
+ function checkedAdd(uint16 a, gtUint16 b) internal returns (gtUint16) {
1283
2237
  (uint256 bit, uint256 res) = ExtendedOperations(address(MPC_PRECOMPILE)).
1284
2238
  CheckedAdd(combineEnumsToBytes3(MPC_TYPE.SUINT16_T, MPC_TYPE.SUINT16_T, ARGS.LHS_PUBLIC), uint256(a), gtUint16.unwrap(b));
1285
2239
 
@@ -1662,6 +2616,791 @@ library MpcCore {
1662
2616
  }
1663
2617
 
1664
2618
 
2619
+ // =========== 128 bit operations ===========
2620
+
2621
+ function add(uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
2622
+ gtUint128 memory result;
2623
+
2624
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2625
+
2626
+ // Add low parts
2627
+ result.low = add(aLow, b.low);
2628
+
2629
+ // Check if there's a carry from low addition
2630
+ gtBool carry = lt(result.low, aLow);
2631
+
2632
+ // Add high parts with carry if needed
2633
+ result.high = add(aHigh, b.high);
2634
+
2635
+ // Add carry to high part if needed
2636
+ result.high = mux(carry, result.high, add(result.high, uint64(1)));
2637
+
2638
+ return result;
2639
+ }
2640
+
2641
+ function checkedAdd(uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
2642
+ gtUint128 memory result;
2643
+
2644
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2645
+
2646
+ // Add low parts
2647
+ result.low = add(aLow, b.low);
2648
+
2649
+ // Check if there's a carry from low addition
2650
+ gtBool carry = lt(result.low, aLow);
2651
+
2652
+ // Add high parts with carry if needed
2653
+ result.high = checkedAdd(aHigh, b.high);
2654
+
2655
+ // Add carry to high part if needed
2656
+ result.high = mux(carry, result.high, checkedAdd(result.high, uint64(1)));
2657
+
2658
+ return result;
2659
+ }
2660
+
2661
+ function checkedAddWithOverflowBit(uint128 a, gtUint128 memory b) internal returns (gtBool, gtUint128 memory) {
2662
+ gtBool bit = setPublic(false);
2663
+ gtUint128 memory result;
2664
+
2665
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2666
+
2667
+ // Add low parts
2668
+ result.low = add(aLow, b.low);
2669
+
2670
+ // Check if there's a carry from low addition
2671
+ gtBool carry = lt(result.low, aLow);
2672
+
2673
+ // Add high parts with carry if needed
2674
+ (gtBool overflow, gtUint64 high) = checkedAddWithOverflowBit(aHigh, b.high);
2675
+ (gtBool overflowWithCarry, gtUint64 highWithCarry) = checkedAddWithOverflowBit(high, uint64(1));
2676
+
2677
+ // Handle carry if needed
2678
+ bit = mux(carry, overflow, or(overflow, overflowWithCarry));
2679
+ result.high = mux(carry, high, highWithCarry);
2680
+
2681
+ return (bit, result);
2682
+ }
2683
+
2684
+ function sub(uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
2685
+ gtUint128 memory result;
2686
+
2687
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2688
+
2689
+ // Subtract low parts
2690
+ result.low = sub(aLow, b.low);
2691
+
2692
+ // Check if there's a borrow from low subtraction
2693
+ gtBool borrow = lt(aLow, b.low);
2694
+
2695
+ // Subtract high parts with borrow if needed
2696
+ result.high = sub(aHigh, b.high);
2697
+
2698
+ // Subtract borrow from high part if needed
2699
+ result.high = mux(borrow, result.high, sub(result.high, uint64(1)));
2700
+
2701
+ return result;
2702
+ }
2703
+
2704
+ function checkedSub(uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
2705
+ gtUint128 memory result;
2706
+
2707
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2708
+
2709
+ // Subtract low parts
2710
+ result.low = sub(aLow, b.low);
2711
+
2712
+ // Check if there's a borrow from low subtraction
2713
+ gtBool borrow = lt(aLow, b.low);
2714
+
2715
+ // Subtract high parts with borrow if needed
2716
+ result.high = checkedSub(aHigh, b.high);
2717
+
2718
+ // Subtract borrow from high part if needed
2719
+ result.high = mux(
2720
+ borrow,
2721
+ result.high,
2722
+ checkedSub(
2723
+ mux(borrow, add(result.high, uint64(1)), result.high),
2724
+ uint64(1)
2725
+ )
2726
+ );
2727
+
2728
+ return result;
2729
+ }
2730
+
2731
+ function checkedSubWithOverflowBit(uint128 a, gtUint128 memory b) internal returns (gtBool, gtUint128 memory) {
2732
+ gtBool bit = setPublic(false);
2733
+ gtUint128 memory result;
2734
+
2735
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2736
+
2737
+ // Subtract low parts
2738
+ result.low = sub(aLow, b.low);
2739
+
2740
+ // Check if there's a borrow from low subtraction
2741
+ gtBool borrow = lt(aLow, b.low);
2742
+
2743
+ // Subtract high parts with borrow if needed
2744
+ (gtBool overflow, gtUint64 high) = checkedSubWithOverflowBit(aHigh, b.high);
2745
+ (gtBool overflowWithCarry, gtUint64 highWithCarry) = checkedSubWithOverflowBit(high, uint64(1));
2746
+
2747
+ // Handle borrow if needed
2748
+ bit = mux(borrow, overflow, or(overflow, overflowWithCarry));
2749
+ result.high = mux(borrow, high, highWithCarry);
2750
+
2751
+ return (bit, result);
2752
+ }
2753
+
2754
+ function _mul64(uint64 a, gtUint64 b) private returns (gtUint64, gtUint64) {
2755
+ uint64 MAX_UINT32 = uint64(type(uint32).max);
2756
+
2757
+ gtUint64 pp0;
2758
+ gtUint64 pp1;
2759
+ gtUint64 pp2;
2760
+ gtUint64 pp3;
2761
+
2762
+ {
2763
+ // Split the numbers into 32-bit parts
2764
+ uint64 aLow = a & type(uint32).max;
2765
+ uint64 aHigh = a >> 32;
2766
+ gtUint64 bLow = and(b, MAX_UINT32);
2767
+ gtUint64 bHigh = shr(b, 32);
2768
+
2769
+ // Compute partial products
2770
+ pp0 = mul(aLow, bLow);
2771
+ pp1 = mul(aLow, bHigh);
2772
+ pp2 = mul(aHigh, bLow);
2773
+ pp3 = mul(aHigh, bHigh);
2774
+ }
2775
+
2776
+ // Compute high and low parts
2777
+ gtUint64 mid = add(
2778
+ add(
2779
+ shr(pp0, 32),
2780
+ and(pp1, MAX_UINT32)
2781
+ ),
2782
+ and(pp2, MAX_UINT32)
2783
+ );
2784
+ gtUint64 carry = shr(mid, 32);
2785
+
2786
+ gtUint64 high = add(
2787
+ add(pp3, shr(pp1, 32)),
2788
+ add(shr(pp2, 32), carry)
2789
+ );
2790
+ gtUint64 low = or(
2791
+ and(pp0, MAX_UINT32),
2792
+ shl(and(mid, MAX_UINT32), 32)
2793
+ );
2794
+
2795
+ return (high, low);
2796
+ }
2797
+
2798
+ function mul(uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
2799
+ gtUint128 memory result;
2800
+
2801
+ uint64 aLow = uint64(a & type(uint64).max);
2802
+ uint64 aHigh = uint64(a >> 64);
2803
+
2804
+ // Compute partial products
2805
+ (gtUint64 pp0, gtUint64 low) = _mul64(aLow, b.low);
2806
+ (, gtUint64 pp1) = _mul64(aHigh, b.low);
2807
+ (, gtUint64 pp2) = _mul64(aLow, b.high);
2808
+
2809
+ // Compute the high and low parts
2810
+ result.high = add(
2811
+ add(pp0, pp1),
2812
+ pp2
2813
+ );
2814
+ result.low = low;
2815
+
2816
+ return result;
2817
+ }
2818
+
2819
+ function checkedMul(uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
2820
+ gtUint128 memory result;
2821
+
2822
+ uint64 aLow = uint64(a & type(uint64).max);
2823
+ uint64 aHigh = uint64(a >> 64);
2824
+
2825
+ // Compute partial products
2826
+ (gtUint64 pp0, gtUint64 low) = _mul64(aLow, b.low);
2827
+ (gtUint64 pp2, gtUint64 pp1) = _mul64(aHigh, b.low);
2828
+ (gtUint64 pp4, gtUint64 pp3) = _mul64(aLow, b.high);
2829
+ (gtUint64 pp6, gtUint64 pp5) = _mul64(aHigh, b.high);
2830
+
2831
+ // Compute the high and low parts
2832
+ result.high = checkedAdd(
2833
+ checkedAdd(pp0, pp1),
2834
+ pp3
2835
+ );
2836
+ result.low = low;
2837
+
2838
+ // Check for overflow
2839
+ checkedSub(
2840
+ uint64(0),
2841
+ add(
2842
+ add(pp2, pp4),
2843
+ add(pp5, pp6)
2844
+ )
2845
+ );
2846
+
2847
+ return result;
2848
+ }
2849
+
2850
+ function checkedMulWithOverflowBit(uint128 a, gtUint128 memory b) internal returns (gtBool, gtUint128 memory) {
2851
+ gtUint128 memory result;
2852
+
2853
+ gtUint64 low;
2854
+ gtUint64 pp0;
2855
+ gtUint64 pp1;
2856
+ gtUint64 pp2;
2857
+ gtUint64 pp3;
2858
+ gtUint64 pp4;
2859
+ gtUint64 pp5;
2860
+ gtUint64 pp6;
2861
+
2862
+ {
2863
+ uint64 aLow = uint64(a & type(uint64).max);
2864
+ uint64 aHigh = uint64(a >> 64);
2865
+
2866
+ // Compute partial products
2867
+ (pp0, low) = _mul64(aLow, b.low);
2868
+ (pp2, pp1) = _mul64(aHigh, b.low);
2869
+ (pp4, pp3) = _mul64(aLow, b.high);
2870
+ (pp6, pp5) = _mul64(aHigh, b.high);
2871
+ }
2872
+
2873
+ // Compute the high and low parts
2874
+ gtBool overflow1;
2875
+
2876
+ (gtBool overflow0, gtUint64 high) = checkedAddWithOverflowBit(pp0, pp1);
2877
+ (overflow1, result.high) = checkedAddWithOverflowBit(high, pp3);
2878
+
2879
+ result.low = low;
2880
+
2881
+ // Check for overflow
2882
+ gtBool bit = or(
2883
+ or(overflow0, overflow1),
2884
+ gt(
2885
+ add(
2886
+ add(pp2, pp4),
2887
+ add(pp5, pp6)
2888
+ ),
2889
+ uint64(0)
2890
+ )
2891
+ );
2892
+
2893
+ return (bit, result);
2894
+ }
2895
+
2896
+ function and(uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
2897
+ gtUint128 memory result;
2898
+
2899
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2900
+
2901
+ result.low = and(aLow, b.low);
2902
+ result.high = and(aHigh, b.high);
2903
+
2904
+ return result;
2905
+ }
2906
+
2907
+ function or(uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
2908
+ gtUint128 memory result;
2909
+
2910
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2911
+
2912
+ result.low = or(aLow, b.low);
2913
+ result.high = or(aHigh, b.high);
2914
+
2915
+ return result;
2916
+ }
2917
+
2918
+ function xor(uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
2919
+ gtUint128 memory result;
2920
+
2921
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2922
+
2923
+ result.low = xor(aLow, b.low);
2924
+ result.high = xor(aHigh, b.high);
2925
+
2926
+ return result;
2927
+ }
2928
+
2929
+ function eq(uint128 a, gtUint128 memory b) internal returns (gtBool) {
2930
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2931
+
2932
+ return and(eq(aLow, b.low), eq(aHigh, b.high));
2933
+ }
2934
+
2935
+ function ne(uint128 a, gtUint128 memory b) internal returns (gtBool) {
2936
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2937
+
2938
+ return or(ne(aLow, b.low), ne(aHigh, b.high));
2939
+ }
2940
+
2941
+ function ge(uint128 a, gtUint128 memory b) internal returns (gtBool) {
2942
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2943
+
2944
+ gtBool highEqual = eq(aHigh, b.high);
2945
+
2946
+ return mux(highEqual, gt(aHigh, b.high), ge(aLow, b.low));
2947
+ }
2948
+
2949
+ function gt(uint128 a, gtUint128 memory b) internal returns (gtBool) {
2950
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2951
+
2952
+ gtBool highEqual = eq(aHigh, b.high);
2953
+
2954
+ return mux(highEqual, gt(aHigh, b.high), gt(aLow, b.low));
2955
+ }
2956
+
2957
+ function le(uint128 a, gtUint128 memory b) internal returns (gtBool) {
2958
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2959
+
2960
+ gtBool highEqual = eq(aHigh, b.high);
2961
+
2962
+ return mux(highEqual, lt(aHigh, b.high), le(aLow, b.low));
2963
+ }
2964
+
2965
+ function lt(uint128 a, gtUint128 memory b) internal returns (gtBool) {
2966
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2967
+
2968
+ gtBool highEqual = eq(aHigh, b.high);
2969
+
2970
+ return mux(highEqual, lt(aHigh, b.high), lt(aLow, b.low));
2971
+ }
2972
+
2973
+ function min(uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
2974
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2975
+
2976
+ gtBool highEqual = eq(aHigh, b.high);
2977
+ gtBool aHighLessThan = lt(aHigh, b.high);
2978
+ gtBool aLowLessThan = lt(aLow, b.low);
2979
+
2980
+ return mux(
2981
+ highEqual,
2982
+ mux(aHighLessThan, b, a),
2983
+ mux(aLowLessThan, b, a)
2984
+ );
2985
+ }
2986
+
2987
+ function max(uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
2988
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
2989
+
2990
+ gtBool highEqual = eq(aHigh, b.high);
2991
+ gtBool aHighGreaterThan = gt(aHigh, b.high);
2992
+ gtBool aLowGreaterThan = gt(aLow, b.low);
2993
+
2994
+ return mux(
2995
+ highEqual,
2996
+ mux(aHighGreaterThan, b, a),
2997
+ mux(aLowGreaterThan, b, a)
2998
+ );
2999
+ }
3000
+
3001
+ function mux(gtBool bit, uint128 a, gtUint128 memory b) internal returns (gtUint128 memory) {
3002
+ gtUint128 memory result;
3003
+
3004
+ (uint64 aHigh, uint64 aLow) = _splitUint128(a);
3005
+
3006
+ result.low = mux(bit, aLow, b.low);
3007
+ result.high = mux(bit, aHigh, b.high);
3008
+
3009
+ return result;
3010
+ }
3011
+
3012
+
3013
+ // =========== 256 bit operations ===========
3014
+
3015
+ function add(uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3016
+ gtUint256 memory result;
3017
+
3018
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3019
+
3020
+ // Add low parts
3021
+ result.low = add(aLow, b.low);
3022
+
3023
+ // Check if there's a carry from low addition
3024
+ gtBool carry = lt(result.low, aLow);
3025
+
3026
+ // Add high parts with carry if needed
3027
+ result.high = add(aHigh, b.high);
3028
+
3029
+ // Add carry to high part if needed
3030
+ result.high = mux(carry, result.high, add(result.high, uint128(1)));
3031
+
3032
+ return result;
3033
+ }
3034
+
3035
+ function checkedAdd(uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3036
+ gtUint256 memory result;
3037
+
3038
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3039
+
3040
+ // Add low parts
3041
+ result.low = add(aLow, b.low);
3042
+
3043
+ // Check if there's a carry from low addition
3044
+ gtBool carry = lt(result.low, aLow);
3045
+
3046
+ // Add high parts with carry if needed
3047
+ result.high = checkedAdd(aHigh, b.high);
3048
+
3049
+ // Add carry to high part if needed
3050
+ result.high = mux(carry, result.high, checkedAdd(result.high, uint128(1)));
3051
+
3052
+ return result;
3053
+ }
3054
+
3055
+ function checkedAddWithOverflowBit(uint256 a, gtUint256 memory b) internal returns (gtBool, gtUint256 memory) {
3056
+ gtBool bit = setPublic(false);
3057
+ gtUint256 memory result;
3058
+
3059
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3060
+
3061
+ // Add low parts
3062
+ result.low = add(aLow, b.low);
3063
+
3064
+ // Check if there's a carry from low addition
3065
+ gtBool carry = lt(result.low, aLow);
3066
+
3067
+ // Add high parts with carry if needed
3068
+ (gtBool overflow, gtUint128 memory high) = checkedAddWithOverflowBit(aHigh, b.high);
3069
+ (gtBool overflowWithCarry, gtUint128 memory highWithCarry) = checkedAddWithOverflowBit(high, uint128(1));
3070
+
3071
+ // Handle carry if needed
3072
+ bit = mux(carry, overflow, or(overflow, overflowWithCarry));
3073
+ result.high = mux(carry, high, highWithCarry);
3074
+
3075
+ return (bit, result);
3076
+ }
3077
+
3078
+ function sub(uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3079
+ gtUint256 memory result;
3080
+
3081
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3082
+
3083
+ // Subtract low parts
3084
+ result.low = sub(aLow, b.low);
3085
+
3086
+ // Check if there's a borrow from low subtraction
3087
+ gtBool borrow = lt(aLow, b.low);
3088
+
3089
+ // Subtract high parts with borrow if needed
3090
+ result.high = sub(aHigh, b.high);
3091
+
3092
+ // Subtract borrow from high part if needed
3093
+ result.high = mux(borrow, result.high, sub(result.high, uint128(1)));
3094
+
3095
+ return result;
3096
+ }
3097
+
3098
+ function checkedSub(uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3099
+ gtUint256 memory result;
3100
+
3101
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3102
+
3103
+ // Subtract low parts
3104
+ result.low = sub(aLow, b.low);
3105
+
3106
+ // Check if there's a borrow from low subtraction
3107
+ gtBool borrow = lt(aLow, b.low);
3108
+
3109
+ // Subtract high parts with borrow if needed
3110
+ result.high = checkedSub(aHigh, b.high);
3111
+
3112
+ // Subtract borrow from high part if needed
3113
+ result.high = mux(
3114
+ borrow,
3115
+ result.high,
3116
+ checkedSub(
3117
+ mux(borrow, add(result.high, uint128(1)), result.high),
3118
+ uint128(1)
3119
+ )
3120
+ );
3121
+
3122
+ return result;
3123
+ }
3124
+
3125
+ function checkedSubWithOverflowBit(uint256 a, gtUint256 memory b) internal returns (gtBool, gtUint256 memory) {
3126
+ gtBool bit = setPublic(false);
3127
+ gtUint256 memory result;
3128
+
3129
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3130
+
3131
+ // Subtract low parts
3132
+ result.low = sub(aLow, b.low);
3133
+
3134
+ // Check if there's a borrow from low subtraction
3135
+ gtBool borrow = lt(aLow, b.low);
3136
+
3137
+ // Subtract high parts with borrow if needed
3138
+ (gtBool overflow, gtUint128 memory high) = checkedSubWithOverflowBit(aHigh, b.high);
3139
+ (gtBool overflowWithCarry, gtUint128 memory highWithCarry) = checkedSubWithOverflowBit(high, uint128(1));
3140
+
3141
+ // Handle borrow if needed
3142
+ bit = mux(borrow, overflow, or(overflow, overflowWithCarry));
3143
+ result.high = mux(borrow, high, highWithCarry);
3144
+
3145
+ return (bit, result);
3146
+ }
3147
+
3148
+ function _mul128(uint128 a, gtUint128 memory b) private returns (gtUint128 memory, gtUint128 memory) {
3149
+ uint128 MAX_UINT64 = uint128(type(uint64).max);
3150
+
3151
+ gtUint128 memory pp0;
3152
+ gtUint128 memory pp1;
3153
+ gtUint128 memory pp2;
3154
+ gtUint128 memory pp3;
3155
+
3156
+ {
3157
+ // Split the numbers into 64-bit parts
3158
+ uint128 aLow = a & type(uint64).max;
3159
+ uint128 aHigh = a >> 64;
3160
+ gtUint128 memory bLow = and(b, MAX_UINT64);
3161
+ gtUint128 memory bHigh = shr(b, 64);
3162
+
3163
+ // Compute partial products
3164
+ pp0 = mul(aLow, bLow);
3165
+ pp1 = mul(aLow, bHigh);
3166
+ pp2 = mul(aHigh, bLow);
3167
+ pp3 = mul(aHigh, bHigh);
3168
+ }
3169
+
3170
+ // Compute high and low parts
3171
+ gtUint128 memory mid = add(
3172
+ add(
3173
+ shr(pp0, 64),
3174
+ and(pp1, MAX_UINT64)
3175
+ ),
3176
+ and(pp2, MAX_UINT64)
3177
+ );
3178
+ gtUint128 memory carry = shr(mid, 64);
3179
+
3180
+ gtUint128 memory high = add(
3181
+ add(pp3, shr(pp1, 64)),
3182
+ add(shr(pp2, 64), carry)
3183
+ );
3184
+ gtUint128 memory low = or(
3185
+ and(pp0, MAX_UINT64),
3186
+ shl(and(mid, MAX_UINT64), 64)
3187
+ );
3188
+
3189
+ return (high, low);
3190
+ }
3191
+
3192
+ function mul(uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3193
+ gtUint256 memory result;
3194
+
3195
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3196
+
3197
+ // Compute partial products
3198
+ (gtUint128 memory pp0, gtUint128 memory low) = _mul128(aLow, b.low);
3199
+ (, gtUint128 memory pp1) = _mul128(aHigh, b.low);
3200
+ (, gtUint128 memory pp2) = _mul128(aLow, b.high);
3201
+
3202
+ // Compute the high and low parts
3203
+ result.high = add(
3204
+ add(pp0, pp1),
3205
+ pp2
3206
+ );
3207
+ result.low = low;
3208
+
3209
+ return result;
3210
+ }
3211
+
3212
+ function checkedMul(uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3213
+ gtUint256 memory result;
3214
+
3215
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3216
+
3217
+ // Compute partial products
3218
+ (gtUint128 memory pp0, gtUint128 memory low) = _mul128(aLow, b.low);
3219
+ (gtUint128 memory pp2, gtUint128 memory pp1) = _mul128(aHigh, b.low);
3220
+ (gtUint128 memory pp4, gtUint128 memory pp3) = _mul128(aLow, b.high);
3221
+ (gtUint128 memory pp6, gtUint128 memory pp5) = _mul128(aHigh, b.high);
3222
+
3223
+ // Compute the high and low parts
3224
+ result.high = checkedAdd(
3225
+ checkedAdd(pp0, pp1),
3226
+ pp3
3227
+ );
3228
+ result.low = low;
3229
+
3230
+ // Check for overflow
3231
+ checkedSub(
3232
+ uint128(0),
3233
+ add(
3234
+ add(pp2, pp4),
3235
+ add(pp5, pp6)
3236
+ )
3237
+ );
3238
+
3239
+ return result;
3240
+ }
3241
+
3242
+ function checkedMulWithOverflowBit(uint256 a, gtUint256 memory b) internal returns (gtBool, gtUint256 memory) {
3243
+ gtUint256 memory result;
3244
+
3245
+ gtUint128 memory low;
3246
+ gtUint128 memory pp0;
3247
+ gtUint128 memory pp1;
3248
+ gtUint128 memory pp2;
3249
+ gtUint128 memory pp3;
3250
+ gtUint128 memory pp4;
3251
+ gtUint128 memory pp5;
3252
+ gtUint128 memory pp6;
3253
+
3254
+ {
3255
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3256
+
3257
+ // Compute partial products
3258
+ (pp0, low) = _mul128(aLow, b.low);
3259
+ (pp2, pp1) = _mul128(aHigh, b.low);
3260
+ (pp4, pp3) = _mul128(aLow, b.high);
3261
+ (pp6, pp5) = _mul128(aHigh, b.high);
3262
+ }
3263
+
3264
+ // Compute the high and low parts
3265
+ gtBool overflow1;
3266
+
3267
+ (gtBool overflow0, gtUint128 memory high) = checkedAddWithOverflowBit(pp0, pp1);
3268
+ (overflow1, result.high) = checkedAddWithOverflowBit(high, pp3);
3269
+
3270
+ result.low = low;
3271
+
3272
+ // Check for overflow
3273
+ gtBool bit = or(
3274
+ or(overflow0, overflow1),
3275
+ gt(
3276
+ add(
3277
+ add(pp2, pp4),
3278
+ add(pp5, pp6)
3279
+ ),
3280
+ uint128(0)
3281
+ )
3282
+ );
3283
+
3284
+ return (bit, result);
3285
+ }
3286
+
3287
+ function and(uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3288
+ gtUint256 memory result;
3289
+
3290
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3291
+
3292
+ result.low = and(aLow, b.low);
3293
+ result.high = and(aHigh, b.high);
3294
+
3295
+ return result;
3296
+ }
3297
+
3298
+ function or(uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3299
+ gtUint256 memory result;
3300
+
3301
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3302
+
3303
+ result.low = or(aLow, b.low);
3304
+ result.high = or(aHigh, b.high);
3305
+
3306
+ return result;
3307
+ }
3308
+
3309
+ function xor(uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3310
+ gtUint256 memory result;
3311
+
3312
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3313
+
3314
+ result.low = xor(aLow, b.low);
3315
+ result.high = xor(aHigh, b.high);
3316
+
3317
+ return result;
3318
+ }
3319
+
3320
+ function eq(uint256 a, gtUint256 memory b) internal returns (gtBool) {
3321
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3322
+
3323
+ return and(eq(aLow, b.low), eq(aHigh, b.high));
3324
+ }
3325
+
3326
+ function ne(uint256 a, gtUint256 memory b) internal returns (gtBool) {
3327
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3328
+
3329
+ return or(ne(aLow, b.low), ne(aHigh, b.high));
3330
+ }
3331
+
3332
+ function ge(uint256 a, gtUint256 memory b) internal returns (gtBool) {
3333
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3334
+
3335
+ gtBool highEqual = eq(aHigh, b.high);
3336
+
3337
+ return mux(highEqual, gt(aHigh, b.high), ge(aLow, b.low));
3338
+ }
3339
+
3340
+ function gt(uint256 a, gtUint256 memory b) internal returns (gtBool) {
3341
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3342
+
3343
+ gtBool highEqual = eq(aHigh, b.high);
3344
+
3345
+ return mux(highEqual, gt(aHigh, b.high), gt(aLow, b.low));
3346
+ }
3347
+
3348
+ function le(uint256 a, gtUint256 memory b) internal returns (gtBool) {
3349
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3350
+
3351
+ gtBool highEqual = eq(aHigh, b.high);
3352
+
3353
+ return mux(highEqual, lt(aHigh, b.high), le(aLow, b.low));
3354
+ }
3355
+
3356
+ function lt(uint256 a, gtUint256 memory b) internal returns (gtBool) {
3357
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3358
+
3359
+ gtBool highEqual = eq(aHigh, b.high);
3360
+
3361
+ return mux(highEqual, lt(aHigh, b.high), lt(aLow, b.low));
3362
+ }
3363
+
3364
+ function min(uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3365
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3366
+
3367
+ gtBool highEqual = eq(aHigh, b.high);
3368
+ gtBool aHighLessThan = lt(aHigh, b.high);
3369
+ gtBool aLowLessThan = lt(aLow, b.low);
3370
+
3371
+ return mux(
3372
+ highEqual,
3373
+ mux(aHighLessThan, b, a),
3374
+ mux(aLowLessThan, b, a)
3375
+ );
3376
+ }
3377
+
3378
+ function max(uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3379
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3380
+
3381
+ gtBool highEqual = eq(aHigh, b.high);
3382
+ gtBool aHighGreaterThan = gt(aHigh, b.high);
3383
+ gtBool aLowGreaterThan = gt(aLow, b.low);
3384
+
3385
+ return mux(
3386
+ highEqual,
3387
+ mux(aHighGreaterThan, b, a),
3388
+ mux(aLowGreaterThan, b, a)
3389
+ );
3390
+ }
3391
+
3392
+ function mux(gtBool bit, uint256 a, gtUint256 memory b) internal returns (gtUint256 memory) {
3393
+ gtUint256 memory result;
3394
+
3395
+ (uint128 aHigh, uint128 aLow) = _splitUint256(a);
3396
+
3397
+ result.low = mux(bit, aLow, b.low);
3398
+ result.high = mux(bit, aHigh, b.high);
3399
+
3400
+ return result;
3401
+ }
3402
+
3403
+
1665
3404
  // =========== Operations with RHS_PUBLIC parameter ===========
1666
3405
  // =========== 8 bit operations ==============
1667
3406
 
@@ -2220,6 +3959,891 @@ library MpcCore {
2220
3959
  Mux(combineEnumsToBytes3(MPC_TYPE.SUINT64_T, MPC_TYPE.SUINT64_T, ARGS.RHS_PUBLIC), gtBool.unwrap(bit), gtUint64.unwrap(a), uint256(b)));
2221
3960
  }
2222
3961
 
3962
+ // =========== 128 bit operations ==============
3963
+
3964
+ function add(gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
3965
+ gtUint128 memory result;
3966
+
3967
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
3968
+
3969
+ // Add low parts
3970
+ result.low = add(a.low, bLow);
3971
+
3972
+ // Check if there's a carry from low addition
3973
+ gtBool carry = lt(result.low, a.low);
3974
+
3975
+ // Add high parts with carry if needed
3976
+ result.high = add(a.high, bHigh);
3977
+
3978
+ // Add carry to high part if needed
3979
+ result.high = mux(carry, result.high, add(result.high, uint64(1)));
3980
+
3981
+ return result;
3982
+ }
3983
+
3984
+ function checkedAdd(gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
3985
+ gtUint128 memory result;
3986
+
3987
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
3988
+
3989
+ // Add low parts
3990
+ result.low = add(a.low, bLow);
3991
+
3992
+ // Check if there's a carry from low addition
3993
+ gtBool carry = lt(result.low, a.low);
3994
+
3995
+ // Add high parts with carry if needed
3996
+ result.high = checkedAdd(a.high, bHigh);
3997
+
3998
+ // Add carry to high part if needed
3999
+ result.high = mux(carry, result.high, checkedAdd(result.high, uint64(1)));
4000
+
4001
+ return result;
4002
+ }
4003
+
4004
+ function checkedAddWithOverflowBit(gtUint128 memory a, uint128 b) internal returns (gtBool, gtUint128 memory) {
4005
+ gtBool bit = setPublic(false);
4006
+ gtUint128 memory result;
4007
+
4008
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4009
+
4010
+ // Add low parts
4011
+ result.low = add(a.low, bLow);
4012
+
4013
+ // Check if there's a carry from low addition
4014
+ gtBool carry = lt(result.low, a.low);
4015
+
4016
+ // Add high parts with carry if needed
4017
+ (gtBool overflow, gtUint64 high) = checkedAddWithOverflowBit(a.high, bHigh);
4018
+ (gtBool overflowWithCarry, gtUint64 highWithCarry) = checkedAddWithOverflowBit(high, uint64(1));
4019
+
4020
+ // Handle carry if needed
4021
+ bit = mux(carry, overflow, or(overflow, overflowWithCarry));
4022
+ result.high = mux(carry, high, highWithCarry);
4023
+
4024
+ return (bit, result);
4025
+ }
4026
+
4027
+ function sub(gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
4028
+ gtUint128 memory result;
4029
+
4030
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4031
+
4032
+ // Subtract low parts
4033
+ result.low = sub(a.low, bLow);
4034
+
4035
+ // Check if there's a borrow from low subtraction
4036
+ gtBool borrow = lt(a.low, bLow);
4037
+
4038
+ // Subtract high parts with borrow if needed
4039
+ result.high = sub(a.high, bHigh);
4040
+
4041
+ // Subtract borrow from high part if needed
4042
+ result.high = mux(borrow, result.high, sub(result.high, uint64(1)));
4043
+
4044
+ return result;
4045
+ }
4046
+
4047
+ function checkedSub(gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
4048
+ gtUint128 memory result;
4049
+
4050
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4051
+
4052
+ // Subtract low parts
4053
+ result.low = sub(a.low, bLow);
4054
+
4055
+ // Check if there's a borrow from low subtraction
4056
+ gtBool borrow = lt(a.low, bLow);
4057
+
4058
+ // Subtract high parts with borrow if needed
4059
+ result.high = checkedSub(a.high, bHigh);
4060
+
4061
+ // Subtract borrow from high part if needed
4062
+ result.high = mux(
4063
+ borrow,
4064
+ result.high,
4065
+ checkedSub(
4066
+ mux(borrow, add(result.high, uint64(1)), result.high),
4067
+ uint64(1)
4068
+ )
4069
+ );
4070
+ return result;
4071
+ }
4072
+
4073
+ function checkedSubWithOverflowBit(gtUint128 memory a, uint128 b) internal returns (gtBool, gtUint128 memory) {
4074
+ gtBool bit = setPublic(false);
4075
+ gtUint128 memory result;
4076
+
4077
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4078
+
4079
+ // Subtract low parts
4080
+ result.low = sub(a.low, bLow);
4081
+
4082
+ // Check if there's a borrow from low subtraction
4083
+ gtBool borrow = lt(a.low, bLow);
4084
+
4085
+ // Subtract high parts with borrow if needed
4086
+ (gtBool overflow, gtUint64 high) = checkedSubWithOverflowBit(a.high, bHigh);
4087
+ (gtBool overflowWithCarry, gtUint64 highWithCarry) = checkedSubWithOverflowBit(high, uint64(1));
4088
+
4089
+ // Handle borrow if needed
4090
+ bit = mux(borrow, overflow, or(overflow, overflowWithCarry));
4091
+ result.high = mux(borrow, high, highWithCarry);
4092
+
4093
+ return (bit, result);
4094
+ }
4095
+
4096
+ function _mul64(gtUint64 a, uint64 b) private returns (gtUint64, gtUint64) {
4097
+ uint64 MAX_UINT32 = uint64(type(uint32).max);
4098
+
4099
+ gtUint64 pp0;
4100
+ gtUint64 pp1;
4101
+ gtUint64 pp2;
4102
+ gtUint64 pp3;
4103
+
4104
+ {
4105
+ // Split the numbers into 32-bit parts
4106
+ gtUint64 aLow = and(a, MAX_UINT32);
4107
+ gtUint64 aHigh = shr(a, 32);
4108
+ uint64 bLow = b & type(uint32).max;
4109
+ uint64 bHigh = b >> 32;
4110
+
4111
+ // Compute partial products
4112
+ pp0 = mul(aLow, bLow);
4113
+ pp1 = mul(aLow, bHigh);
4114
+ pp2 = mul(aHigh, bLow);
4115
+ pp3 = mul(aHigh, bHigh);
4116
+ }
4117
+
4118
+ // Compute high and low parts
4119
+ gtUint64 mid = add(
4120
+ add(
4121
+ shr(pp0, 32),
4122
+ and(pp1, MAX_UINT32)
4123
+ ),
4124
+ and(pp2, MAX_UINT32)
4125
+ );
4126
+ gtUint64 carry = shr(mid, 32);
4127
+
4128
+ gtUint64 high = add(
4129
+ add(pp3, shr(pp1, 32)),
4130
+ add(shr(pp2, 32), carry)
4131
+ );
4132
+ gtUint64 low = or(
4133
+ and(pp0, MAX_UINT32),
4134
+ shl(and(mid, MAX_UINT32), 32)
4135
+ );
4136
+
4137
+ return (high, low);
4138
+ }
4139
+
4140
+ function mul(gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
4141
+ gtUint128 memory result;
4142
+
4143
+ uint64 bLow = uint64(b & type(uint64).max);
4144
+ uint64 bHigh = uint64(b >> 64);
4145
+
4146
+ // Compute partial products
4147
+ (gtUint64 pp0, gtUint64 low) = _mul64(a.low, bLow);
4148
+ (, gtUint64 pp1) = _mul64(a.high, bLow);
4149
+ (, gtUint64 pp2) = _mul64(a.low, bHigh);
4150
+
4151
+ // Compute the high and low parts
4152
+ result.high = add(
4153
+ add(pp0, pp1),
4154
+ pp2
4155
+ );
4156
+ result.low = low;
4157
+
4158
+ return result;
4159
+ }
4160
+
4161
+ function checkedMul(gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
4162
+ gtUint128 memory result;
4163
+
4164
+ uint64 bLow = uint64(b & type(uint64).max);
4165
+ uint64 bHigh = uint64(b >> 64);
4166
+
4167
+ // Compute partial products
4168
+ (gtUint64 pp0, gtUint64 low) = _mul64(a.low, bLow);
4169
+ (gtUint64 pp2, gtUint64 pp1) = _mul64(a.high, bLow);
4170
+ (gtUint64 pp4, gtUint64 pp3) = _mul64(a.low, bHigh);
4171
+ (gtUint64 pp6, gtUint64 pp5) = _mul64(a.high, bHigh);
4172
+
4173
+ // Compute the high and low parts
4174
+ result.high = checkedAdd(
4175
+ checkedAdd(pp0, pp1),
4176
+ pp3
4177
+ );
4178
+ result.low = low;
4179
+
4180
+ // Check for overflow
4181
+ checkedSub(
4182
+ uint64(0),
4183
+ add(
4184
+ add(pp2, pp4),
4185
+ add(pp5, pp6)
4186
+ )
4187
+ );
4188
+
4189
+ return result;
4190
+ }
4191
+
4192
+ function checkedMulWithOverflowBit(gtUint128 memory a, uint128 b) internal returns (gtBool, gtUint128 memory) {
4193
+ gtUint128 memory result;
4194
+
4195
+ gtUint64 low;
4196
+ gtUint64 pp0;
4197
+ gtUint64 pp1;
4198
+ gtUint64 pp2;
4199
+ gtUint64 pp3;
4200
+ gtUint64 pp4;
4201
+ gtUint64 pp5;
4202
+ gtUint64 pp6;
4203
+
4204
+ {
4205
+ uint64 bLow = uint64(b & type(uint64).max);
4206
+ uint64 bHigh = uint64(b >> 64);
4207
+
4208
+ // Compute partial products
4209
+ (pp0, low) = _mul64(a.low, bLow);
4210
+ (pp2, pp1) = _mul64(a.high, bLow);
4211
+ (pp4, pp3) = _mul64(a.low, bHigh);
4212
+ (pp6, pp5) = _mul64(a.high, bHigh);
4213
+ }
4214
+
4215
+ // Compute the high and low parts
4216
+ gtBool overflow1;
4217
+
4218
+ (gtBool overflow0, gtUint64 high) = checkedAddWithOverflowBit(pp0, pp1);
4219
+ (overflow1, result.high) = checkedAddWithOverflowBit(high, pp3);
4220
+
4221
+ result.low = low;
4222
+
4223
+ // Check for overflow
4224
+ gtBool bit = or(
4225
+ or(overflow0, overflow1),
4226
+ gt(
4227
+ add(
4228
+ add(pp2, pp4),
4229
+ add(pp5, pp6)
4230
+ ),
4231
+ uint64(0)
4232
+ )
4233
+ );
4234
+
4235
+ return (bit, result);
4236
+ }
4237
+
4238
+ function and(gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
4239
+ gtUint128 memory result;
4240
+
4241
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4242
+
4243
+ result.low = and(a.low, bLow);
4244
+ result.high = and(a.high, bHigh);
4245
+
4246
+ return result;
4247
+ }
4248
+
4249
+ function or(gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
4250
+ gtUint128 memory result;
4251
+
4252
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4253
+
4254
+ result.low = or(a.low, bLow);
4255
+ result.high = or(a.high, bHigh);
4256
+
4257
+ return result;
4258
+ }
4259
+
4260
+ function xor(gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
4261
+ gtUint128 memory result;
4262
+
4263
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4264
+
4265
+ result.low = xor(a.low, bLow);
4266
+ result.high = xor(a.high, bHigh);
4267
+
4268
+ return result;
4269
+ }
4270
+
4271
+ function shl(gtUint128 memory a, uint8 b) internal returns (gtUint128 memory) {
4272
+ gtUint128 memory result;
4273
+
4274
+ if (b >= 64) {
4275
+ shl(a.high, b); // check for overflow in high part
4276
+
4277
+ result.low = setPublic64(0);
4278
+ result.high = shl(a.low, b - 64);
4279
+ } else if (b > 0) {
4280
+ // Mask to clear the bits of the low part that will be shifted out
4281
+ uint64 mask = uint64(type(uint64).max >> b);
4282
+
4283
+ result.low = shl(and(a.low, mask), b);
4284
+
4285
+ // Mask to clear the bits of the low part that remain in the low part
4286
+ mask = uint64(type(uint64).max << 64 - b);
4287
+
4288
+ result.high = or(shl(a.high, b), shr(and(a.low, mask), 64 - b));
4289
+ } else {
4290
+ result.low = a.low;
4291
+ result.high = a.high;
4292
+ }
4293
+
4294
+ return result;
4295
+ }
4296
+
4297
+ function shr(gtUint128 memory a, uint8 b) internal returns (gtUint128 memory) {
4298
+ gtUint128 memory result;
4299
+
4300
+ if (b >= 64) {
4301
+ shr(a.low, b); // check for overflow in low part
4302
+
4303
+ result.low = shr(a.high, b - 64);
4304
+ result.high = setPublic64(0);
4305
+ } else if (b > 0) {
4306
+ // Mask to clear the bits of the low part that will be shifted out
4307
+ uint64 mask = uint64(type(uint64).max >> 64 - b);
4308
+
4309
+ result.low = or(shr(a.low, b), shl(and(a.high, mask), 64 - b));
4310
+
4311
+ // Mask to clear the bits of the low part that remain in the low part
4312
+ mask = uint64(type(uint64).max << b);
4313
+
4314
+ result.high = shr(and(a.high, mask), b);
4315
+ } else {
4316
+ result.low = a.low;
4317
+ result.high = a.high;
4318
+ }
4319
+
4320
+ return result;
4321
+ }
4322
+
4323
+ function eq(gtUint128 memory a, uint128 b) internal returns (gtBool) {
4324
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4325
+
4326
+ return and(eq(a.low, bLow), eq(a.high, bHigh));
4327
+ }
4328
+
4329
+ function ne(gtUint128 memory a, uint128 b) internal returns (gtBool) {
4330
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4331
+
4332
+ return or(ne(a.low, bLow), ne(a.high, bHigh));
4333
+ }
4334
+
4335
+ function ge(gtUint128 memory a, uint128 b) internal returns (gtBool) {
4336
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4337
+
4338
+ gtBool highEqual = eq(a.high, bHigh);
4339
+
4340
+ return mux(highEqual, gt(a.high, bHigh), ge(a.low, bLow));
4341
+ }
4342
+
4343
+ function gt(gtUint128 memory a, uint128 b) internal returns (gtBool) {
4344
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4345
+
4346
+ gtBool highEqual = eq(a.high, bHigh);
4347
+
4348
+ return mux(highEqual, gt(a.high, bHigh), gt(a.low, bLow));
4349
+ }
4350
+
4351
+ function le(gtUint128 memory a, uint128 b) internal returns (gtBool) {
4352
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4353
+
4354
+ gtBool highEqual = eq(a.high, bHigh);
4355
+
4356
+ return mux(highEqual, lt(a.high, bHigh), le(a.low, bLow));
4357
+ }
4358
+
4359
+ function lt(gtUint128 memory a, uint128 b) internal returns (gtBool) {
4360
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4361
+
4362
+ gtBool highEqual = eq(a.high, bHigh);
4363
+
4364
+ return mux(highEqual, lt(a.high, bHigh), lt(a.low, bLow));
4365
+ }
4366
+
4367
+ function min(gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
4368
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4369
+
4370
+ gtBool highEqual = eq(a.high, bHigh);
4371
+ gtBool aHighLessThan = lt(a.high, bHigh);
4372
+ gtBool aLowLessThan = lt(a.low, bLow);
4373
+
4374
+ return mux(
4375
+ highEqual,
4376
+ mux(aHighLessThan, b, a),
4377
+ mux(aLowLessThan, b, a)
4378
+ );
4379
+ }
4380
+
4381
+ function max(gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
4382
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4383
+
4384
+ gtBool highEqual = eq(a.high, bHigh);
4385
+ gtBool aHighGreaterThan = gt(a.high, bHigh);
4386
+ gtBool aLowGreaterThan = gt(a.low, bLow);
4387
+
4388
+ return mux(
4389
+ highEqual,
4390
+ mux(aHighGreaterThan, b, a),
4391
+ mux(aLowGreaterThan, b, a)
4392
+ );
4393
+ }
4394
+
4395
+ function mux(gtBool bit, gtUint128 memory a, uint128 b) internal returns (gtUint128 memory) {
4396
+ gtUint128 memory result;
4397
+
4398
+ (uint64 bHigh, uint64 bLow) = _splitUint128(b);
4399
+
4400
+ result.low = mux(bit, a.low, bLow);
4401
+ result.high = mux(bit, a.high, bHigh);
4402
+
4403
+ return result;
4404
+ }
4405
+
4406
+ // =========== 256 bit operations ==============
4407
+
4408
+ function add(gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4409
+ gtUint256 memory result;
4410
+
4411
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4412
+
4413
+ // Add low parts
4414
+ result.low = add(a.low, bLow);
4415
+
4416
+ // Check if there's a carry from low addition
4417
+ gtBool carry = lt(result.low, a.low);
4418
+
4419
+ // Add high parts with carry if needed
4420
+ result.high = add(a.high, bHigh);
4421
+
4422
+ // Add carry to high part if needed
4423
+ result.high = mux(carry, result.high, add(result.high, uint128(1)));
4424
+
4425
+ return result;
4426
+ }
4427
+
4428
+ function checkedAdd(gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4429
+ gtUint256 memory result;
4430
+
4431
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4432
+
4433
+ // Add low parts
4434
+ result.low = add(a.low, bLow);
4435
+
4436
+ // Check if there's a carry from low addition
4437
+ gtBool carry = lt(result.low, a.low);
4438
+
4439
+ // Add high parts with carry if needed
4440
+ result.high = checkedAdd(a.high, bHigh);
4441
+
4442
+ // Add carry to high part if needed
4443
+ result.high = mux(carry, result.high, checkedAdd(result.high, uint128(1)));
4444
+
4445
+ return result;
4446
+ }
4447
+
4448
+ function checkedAddWithOverflowBit(gtUint256 memory a, uint256 b) internal returns (gtBool, gtUint256 memory) {
4449
+ gtBool bit = setPublic(false);
4450
+ gtUint256 memory result;
4451
+
4452
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4453
+
4454
+ // Add low parts
4455
+ result.low = add(a.low, bLow);
4456
+
4457
+ // Check if there's a carry from low addition
4458
+ gtBool carry = lt(result.low, a.low);
4459
+
4460
+ // Add high parts with carry if needed
4461
+ (gtBool overflow, gtUint128 memory high) = checkedAddWithOverflowBit(a.high, bHigh);
4462
+ (gtBool overflowWithCarry, gtUint128 memory highWithCarry) = checkedAddWithOverflowBit(high, uint128(1));
4463
+
4464
+ // Handle carry if needed
4465
+ bit = mux(carry, overflow, or(overflow, overflowWithCarry));
4466
+ result.high = mux(carry, high, highWithCarry);
4467
+
4468
+ return (bit, result);
4469
+ }
4470
+
4471
+ function sub(gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4472
+ gtUint256 memory result;
4473
+
4474
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4475
+
4476
+ // Subtract low parts
4477
+ result.low = sub(a.low, bLow);
4478
+
4479
+ // Check if there's a borrow from low subtraction
4480
+ gtBool borrow = lt(a.low, bLow);
4481
+
4482
+ // Subtract high parts with borrow if needed
4483
+ result.high = sub(a.high, bHigh);
4484
+
4485
+ // Subtract borrow from high part if needed
4486
+ result.high = mux(borrow, result.high, sub(result.high, uint128(1)));
4487
+
4488
+ return result;
4489
+ }
4490
+
4491
+ function checkedSub(gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4492
+ gtUint256 memory result;
4493
+
4494
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4495
+
4496
+ // Subtract low parts
4497
+ result.low = sub(a.low, bLow);
4498
+
4499
+ // Check if there's a borrow from low subtraction
4500
+ gtBool borrow = lt(a.low, bLow);
4501
+
4502
+ // Subtract high parts with borrow if needed
4503
+ result.high = checkedSub(a.high, bHigh);
4504
+
4505
+ // Subtract borrow from high part if needed
4506
+ result.high = mux(
4507
+ borrow,
4508
+ result.high,
4509
+ checkedSub(
4510
+ mux(borrow, add(result.high, uint128(1)), result.high),
4511
+ uint128(1)
4512
+ )
4513
+ );
4514
+ return result;
4515
+ }
4516
+
4517
+ function checkedSubWithOverflowBit(gtUint256 memory a, uint256 b) internal returns (gtBool, gtUint256 memory) {
4518
+ gtBool bit = setPublic(false);
4519
+ gtUint256 memory result;
4520
+
4521
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4522
+
4523
+ // Subtract low parts
4524
+ result.low = sub(a.low, bLow);
4525
+
4526
+ // Check if there's a borrow from low subtraction
4527
+ gtBool borrow = lt(a.low, bLow);
4528
+
4529
+ // Subtract high parts with borrow if needed
4530
+ (gtBool overflow, gtUint128 memory high) = checkedSubWithOverflowBit(a.high, bHigh);
4531
+ (gtBool overflowWithCarry, gtUint128 memory highWithCarry) = checkedSubWithOverflowBit(high, uint128(1));
4532
+
4533
+ // Handle borrow if needed
4534
+ bit = mux(borrow, overflow, or(overflow, overflowWithCarry));
4535
+ result.high = mux(borrow, high, highWithCarry);
4536
+
4537
+ return (bit, result);
4538
+ }
4539
+
4540
+ function _mul128(gtUint128 memory a, uint128 b) private returns (gtUint128 memory, gtUint128 memory) {
4541
+ uint128 MAX_UINT64 = uint128(type(uint64).max);
4542
+
4543
+ gtUint128 memory pp0;
4544
+ gtUint128 memory pp1;
4545
+ gtUint128 memory pp2;
4546
+ gtUint128 memory pp3;
4547
+
4548
+ {
4549
+ // Split the numbers into 64-bit parts
4550
+ gtUint128 memory aLow = and(a, MAX_UINT64);
4551
+ gtUint128 memory aHigh = shr(a, 64);
4552
+ uint128 bLow = b & type(uint64).max;
4553
+ uint128 bHigh = b >> 64;
4554
+
4555
+ // Compute partial products
4556
+ pp0 = mul(aLow, bLow);
4557
+ pp1 = mul(aLow, bHigh);
4558
+ pp2 = mul(aHigh, bLow);
4559
+ pp3 = mul(aHigh, bHigh);
4560
+ }
4561
+
4562
+ // Compute high and low parts
4563
+ gtUint128 memory mid = add(
4564
+ add(
4565
+ shr(pp0, 64),
4566
+ and(pp1, MAX_UINT64)
4567
+ ),
4568
+ and(pp2, MAX_UINT64)
4569
+ );
4570
+ gtUint128 memory carry = shr(mid, 64);
4571
+
4572
+ gtUint128 memory high = add(
4573
+ add(pp3, shr(pp1, 64)),
4574
+ add(shr(pp2, 64), carry)
4575
+ );
4576
+ gtUint128 memory low = or(
4577
+ and(pp0, MAX_UINT64),
4578
+ shl(and(mid, MAX_UINT64), 64)
4579
+ );
4580
+
4581
+ return (high, low);
4582
+ }
4583
+
4584
+ function mul(gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4585
+ gtUint256 memory result;
4586
+
4587
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4588
+
4589
+ // Compute partial products
4590
+ (gtUint128 memory pp0, gtUint128 memory low) = _mul128(a.low, bLow);
4591
+ (, gtUint128 memory pp1) = _mul128(a.high, bLow);
4592
+ (, gtUint128 memory pp2) = _mul128(a.low, bHigh);
4593
+
4594
+ // Compute the high and low parts
4595
+ result.high = add(
4596
+ add(pp0, pp1),
4597
+ pp2
4598
+ );
4599
+ result.low = low;
4600
+
4601
+ return result;
4602
+ }
4603
+
4604
+ function checkedMul(gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4605
+ gtUint256 memory result;
4606
+
4607
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4608
+
4609
+ // Compute partial products
4610
+ (gtUint128 memory pp0, gtUint128 memory low) = _mul128(a.low, bLow);
4611
+ (gtUint128 memory pp2, gtUint128 memory pp1) = _mul128(a.high, bLow);
4612
+ (gtUint128 memory pp4, gtUint128 memory pp3) = _mul128(a.low, bHigh);
4613
+ (gtUint128 memory pp6, gtUint128 memory pp5) = _mul128(a.high, bHigh);
4614
+
4615
+ // Compute the high and low parts
4616
+ result.high = checkedAdd(
4617
+ checkedAdd(pp0, pp1),
4618
+ pp3
4619
+ );
4620
+ result.low = low;
4621
+
4622
+ // Check for overflow
4623
+ checkedSub(
4624
+ uint128(0),
4625
+ add(
4626
+ add(pp2, pp4),
4627
+ add(pp5, pp6)
4628
+ )
4629
+ );
4630
+
4631
+ return result;
4632
+ }
4633
+
4634
+ function checkedMulWithOverflowBit(gtUint256 memory a, uint256 b) internal returns (gtBool, gtUint256 memory) {
4635
+ gtUint256 memory result;
4636
+
4637
+ gtUint128 memory low;
4638
+ gtUint128 memory pp0;
4639
+ gtUint128 memory pp1;
4640
+ gtUint128 memory pp2;
4641
+ gtUint128 memory pp3;
4642
+ gtUint128 memory pp4;
4643
+ gtUint128 memory pp5;
4644
+ gtUint128 memory pp6;
4645
+
4646
+ {
4647
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4648
+
4649
+ // Compute partial products
4650
+ (pp0, low) = _mul128(a.low, bLow);
4651
+ (pp2, pp1) = _mul128(a.high, bLow);
4652
+ (pp4, pp3) = _mul128(a.low, bHigh);
4653
+ (pp6, pp5) = _mul128(a.high, bHigh);
4654
+ }
4655
+
4656
+ // Compute the high and low parts
4657
+ gtBool overflow1;
4658
+
4659
+ (gtBool overflow0, gtUint128 memory high) = checkedAddWithOverflowBit(pp0, pp1);
4660
+ (overflow1, result.high) = checkedAddWithOverflowBit(high, pp3);
4661
+
4662
+ result.low = low;
4663
+
4664
+ // Check for overflow
4665
+ gtBool bit = or(
4666
+ or(overflow0, overflow1),
4667
+ gt(
4668
+ add(
4669
+ add(pp2, pp4),
4670
+ add(pp5, pp6)
4671
+ ),
4672
+ uint128(0)
4673
+ )
4674
+ );
4675
+
4676
+ return (bit, result);
4677
+ }
4678
+
4679
+ function and(gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4680
+ gtUint256 memory result;
4681
+
4682
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4683
+
4684
+ result.low = and(a.low, bLow);
4685
+ result.high = and(a.high, bHigh);
4686
+
4687
+ return result;
4688
+ }
4689
+
4690
+ function or(gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4691
+ gtUint256 memory result;
4692
+
4693
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4694
+
4695
+ result.low = or(a.low, bLow);
4696
+ result.high = or(a.high, bHigh);
4697
+
4698
+ return result;
4699
+ }
4700
+
4701
+ function xor(gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4702
+ gtUint256 memory result;
4703
+
4704
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4705
+
4706
+ result.low = xor(a.low, bLow);
4707
+ result.high = xor(a.high, bHigh);
4708
+
4709
+ return result;
4710
+ }
4711
+
4712
+ function shl(gtUint256 memory a, uint8 b) internal returns (gtUint256 memory) {
4713
+ gtUint256 memory result;
4714
+
4715
+ if (b >= 128) {
4716
+ shl(a.high, b); // check for overflow in high part
4717
+
4718
+ result.low = setPublic128(0);
4719
+ result.high = shl(a.low, b - 128);
4720
+ } else if (b > 0) {
4721
+ // Mask to clear the bits of the low part that will be shifted out
4722
+ uint128 mask = uint128(type(uint128).max >> b);
4723
+
4724
+ result.low = shl(and(a.low, mask), b);
4725
+
4726
+ // Mask to clear the bits of the low part that remain in the low part
4727
+ mask = uint128(type(uint128).max << 128 - b);
4728
+
4729
+ result.high = or(shl(a.high, b), shr(and(a.low, mask), 128 - b));
4730
+ } else {
4731
+ result.low = a.low;
4732
+ result.high = a.high;
4733
+ }
4734
+
4735
+ return result;
4736
+ }
4737
+
4738
+ function shr(gtUint256 memory a, uint8 b) internal returns (gtUint256 memory) {
4739
+ gtUint256 memory result;
4740
+
4741
+ if (b >= 128) {
4742
+ shr(a.low, b); // check for overflow in low part
4743
+
4744
+ result.low = shr(a.high, b - 128);
4745
+ result.high = setPublic128(0);
4746
+ } else if (b > 0) {
4747
+ // Mask to clear the bits of the low part that will be shifted out
4748
+ uint128 mask = uint128(type(uint128).max >> 128 - b);
4749
+
4750
+ result.low = or(shr(a.low, b), shl(and(a.high, mask), 128 - b));
4751
+
4752
+ // Mask to clear the bits of the low part that remain in the low part
4753
+ mask = uint128(type(uint128).max << b);
4754
+
4755
+ result.high = shr(and(a.high, mask), b);
4756
+ } else {
4757
+ result.low = a.low;
4758
+ result.high = a.high;
4759
+ }
4760
+
4761
+ return result;
4762
+ }
4763
+
4764
+ function eq(gtUint256 memory a, uint256 b) internal returns (gtBool) {
4765
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4766
+
4767
+ return and(eq(a.low, bLow), eq(a.high, bHigh));
4768
+ }
4769
+
4770
+ function ne(gtUint256 memory a, uint256 b) internal returns (gtBool) {
4771
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4772
+
4773
+ return or(ne(a.low, bLow), ne(a.high, bHigh));
4774
+ }
4775
+
4776
+ function ge(gtUint256 memory a, uint256 b) internal returns (gtBool) {
4777
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4778
+
4779
+ gtBool highEqual = eq(a.high, bHigh);
4780
+
4781
+ return mux(highEqual, gt(a.high, bHigh), ge(a.low, bLow));
4782
+ }
4783
+
4784
+ function gt(gtUint256 memory a, uint256 b) internal returns (gtBool) {
4785
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4786
+
4787
+ gtBool highEqual = eq(a.high, bHigh);
4788
+
4789
+ return mux(highEqual, gt(a.high, bHigh), gt(a.low, bLow));
4790
+ }
4791
+
4792
+ function le(gtUint256 memory a, uint256 b) internal returns (gtBool) {
4793
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4794
+
4795
+ gtBool highEqual = eq(a.high, bHigh);
4796
+
4797
+ return mux(highEqual, lt(a.high, bHigh), le(a.low, bLow));
4798
+ }
4799
+
4800
+ function lt(gtUint256 memory a, uint256 b) internal returns (gtBool) {
4801
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4802
+
4803
+ gtBool highEqual = eq(a.high, bHigh);
4804
+
4805
+ return mux(highEqual, lt(a.high, bHigh), lt(a.low, bLow));
4806
+ }
4807
+
4808
+ function min(gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4809
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4810
+
4811
+ gtBool highEqual = eq(a.high, bHigh);
4812
+ gtBool aHighLessThan = lt(a.high, bHigh);
4813
+ gtBool aLowLessThan = lt(a.low, bLow);
4814
+
4815
+ return mux(
4816
+ highEqual,
4817
+ mux(aHighLessThan, b, a),
4818
+ mux(aLowLessThan, b, a)
4819
+ );
4820
+ }
4821
+
4822
+ function max(gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4823
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4824
+
4825
+ gtBool highEqual = eq(a.high, bHigh);
4826
+ gtBool aHighGreaterThan = gt(a.high, bHigh);
4827
+ gtBool aLowGreaterThan = gt(a.low, bLow);
4828
+
4829
+ return mux(
4830
+ highEqual,
4831
+ mux(aHighGreaterThan, b, a),
4832
+ mux(aLowGreaterThan, b, a)
4833
+ );
4834
+ }
4835
+
4836
+ function mux(gtBool bit, gtUint256 memory a, uint256 b) internal returns (gtUint256 memory) {
4837
+ gtUint256 memory result;
4838
+
4839
+ (uint128 bHigh, uint128 bLow) = _splitUint256(b);
4840
+
4841
+ result.low = mux(bit, a.low, bLow);
4842
+ result.high = mux(bit, a.high, bHigh);
4843
+
4844
+ return result;
4845
+ }
4846
+
2223
4847
  // In the context of a transfer, scalar balances are irrelevant;
2224
4848
  // The only possibility for a scalar value is within the "amount" parameter.
2225
4849
  // Therefore, in this scenario, LHS_PUBLIC signifies a scalar amount, not balance1.