@inco/lightning 0.1.30 → 0.1.32

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 (41) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/manifest.yaml +23 -0
  3. package/package.json +5 -1
  4. package/src/CreateXHelper.sol +14 -0
  5. package/src/DeployUtils.sol +95 -19
  6. package/src/Lib.demonet.sol +389 -0
  7. package/src/Lib.devnet.sol +389 -0
  8. package/src/Lib.sol +37 -160
  9. package/src/Lib.testnet.sol +389 -0
  10. package/src/Types.sol +54 -52
  11. package/src/libs/incoLightning_0_1_29__863421733.sol +389 -0
  12. package/src/lightning-parts/DecryptionHandler.sol +208 -37
  13. package/src/lightning-parts/primitives/SignatureVerifier.sol +24 -9
  14. package/src/test/AddTwo.sol +15 -11
  15. package/src/test/FakeIncoInfra/FakeIncoInfraBase.sol +2 -1
  16. package/src/test/FibonacciDecrypt.sol +48 -0
  17. package/src/test/IncoTest.sol +6 -4
  18. package/src/test/TestAddTwo.t.sol +8 -10
  19. package/src/version/IncoLightningConfig.sol +2 -2
  20. package/dumps/incoLightning_0_1_23__547622051.dump.json +0 -1
  21. package/dumps/incoLightning_0_1_23__547622051.env +0 -15
  22. package/dumps/incoLightning_0_1_23__830342853.dump.json +0 -1
  23. package/dumps/incoLightning_0_1_23__830342853.env +0 -15
  24. package/dumps/incoLightning_0_1_24__266705097.dump.json +0 -1
  25. package/dumps/incoLightning_0_1_24__266705097.env +0 -15
  26. package/dumps/incoLightning_0_1_25__861473222.dump.json +0 -1
  27. package/dumps/incoLightning_0_1_25__861473222.env +0 -15
  28. package/dumps/incoLightning_0_1_25__986372984.dump.json +0 -1
  29. package/dumps/incoLightning_0_1_25__986372984.env +0 -15
  30. package/dumps/incoLightning_0_1_26__18043964.dump.json +0 -1
  31. package/dumps/incoLightning_0_1_26__18043964.env +0 -15
  32. package/dumps/incoLightning_0_1_26__444235330.dump.json +0 -1
  33. package/dumps/incoLightning_0_1_26__444235330.env +0 -15
  34. package/dumps/incoLightning_0_1_27__125335042.dump.json +0 -1
  35. package/dumps/incoLightning_0_1_27__125335042.env +0 -14
  36. package/dumps/incoLightning_0_1_27__558243565.dump.json +0 -1
  37. package/dumps/incoLightning_0_1_27__558243565.env +0 -14
  38. package/dumps/incoLightning_0_1_29__183408998.dump.json +0 -1
  39. package/dumps/incoLightning_0_1_29__183408998.env +0 -14
  40. package/dumps/incoLightning_0_1_29__340846814.dump.json +0 -1
  41. package/dumps/incoLightning_0_1_29__340846814.env +0 -14
@@ -0,0 +1,389 @@
1
+ // AUTOGENERATED FILE. DO NOT EDIT.
2
+ // This file was generated by the IncoLightning library generator.
3
+ // The original template is located at Lib.template.sol
4
+
5
+ /// SPDX-License-Identifier: No License
6
+ pragma solidity ^0.8;
7
+
8
+ import "../IncoLightning.sol";
9
+ import { ebool, euint256, ETypes } from "../Types.sol";
10
+
11
+ IncoLightning constant inco = IncoLightning(0xeBAFF6D578733E4603b99CBdbb221482F29a78E1);
12
+ address constant deployedBy = 0x8202D2D747784Cb7D48868E44C42C4bf162a70BC;
13
+ uint256 constant defaultDecryptionDelayLimit = 2 hours;
14
+
15
+ library e {
16
+ function sanitize(euint256 a) internal returns (euint256) {
17
+ if (euint256.unwrap(a) == bytes32(0)) {
18
+ return asEuint256(0);
19
+ }
20
+ return a;
21
+ }
22
+
23
+ function sanitize(ebool a) internal returns (ebool) {
24
+ if (ebool.unwrap(a) == bytes32(0)) {
25
+ return asEbool(false);
26
+ }
27
+ return a;
28
+ }
29
+
30
+ function s(euint256 a) internal returns (euint256) {
31
+ return sanitize(a);
32
+ }
33
+
34
+ function s(ebool a) internal returns (ebool) {
35
+ return sanitize(a);
36
+ }
37
+
38
+ function add(euint256 a, euint256 b) internal returns (euint256) {
39
+ return inco.eAdd(s(a), s(b));
40
+ }
41
+
42
+ function add(euint256 a, uint256 b) internal returns (euint256) {
43
+ return inco.eAdd(s(a), asEuint256(b));
44
+ }
45
+
46
+ function add(uint256 a, euint256 b) internal returns (euint256) {
47
+ return inco.eAdd(asEuint256(a), s(b));
48
+ }
49
+
50
+ function sub(euint256 a, euint256 b) internal returns (euint256) {
51
+ return inco.eSub(s(a), s(b));
52
+ }
53
+
54
+ function sub(euint256 a, uint256 b) internal returns (euint256) {
55
+ return inco.eSub(s(a), asEuint256(b));
56
+ }
57
+
58
+ function sub(uint256 a, euint256 b) internal returns (euint256) {
59
+ return inco.eSub(asEuint256(a), s(b));
60
+ }
61
+
62
+ function mul(euint256 a, euint256 b) internal returns (euint256) {
63
+ return inco.eMul(s(a), s(b));
64
+ }
65
+
66
+ function mul(euint256 a, uint256 b) internal returns (euint256) {
67
+ return inco.eMul(s(a), asEuint256(b));
68
+ }
69
+
70
+ function mul(uint256 a, euint256 b) internal returns (euint256) {
71
+ return inco.eMul(asEuint256(a), s(b));
72
+ }
73
+
74
+ function div(euint256 a, euint256 b) internal returns (euint256) {
75
+ return inco.eDiv(s(a), s(b));
76
+ }
77
+
78
+ function div(euint256 a, uint256 b) internal returns (euint256) {
79
+ return inco.eDiv(s(a), asEuint256(b));
80
+ }
81
+
82
+ function div(uint256 a, euint256 b) internal returns (euint256) {
83
+ return inco.eDiv(asEuint256(a), s(b));
84
+ }
85
+
86
+ function rem(euint256 a, euint256 b) internal returns (euint256) {
87
+ return inco.eRem(s(a), s(b));
88
+ }
89
+
90
+ function rem(euint256 a, uint256 b) internal returns (euint256) {
91
+ return inco.eRem(s(a), asEuint256(b));
92
+ }
93
+
94
+ function rem(uint256 a, euint256 b) internal returns (euint256) {
95
+ return inco.eRem(asEuint256(a), s(b));
96
+ }
97
+
98
+ function and(euint256 a, euint256 b) internal returns (euint256) {
99
+ return euint256.wrap(inco.eBitAnd(euint256.unwrap(s(a)), euint256.unwrap(s(b))));
100
+ }
101
+
102
+ function and(euint256 a, uint256 b) internal returns (euint256) {
103
+ return euint256.wrap(inco.eBitAnd(euint256.unwrap(s(a)), euint256.unwrap(asEuint256(b))));
104
+ }
105
+
106
+ function and(uint256 a, euint256 b) internal returns (euint256) {
107
+ return euint256.wrap(inco.eBitAnd(euint256.unwrap(asEuint256(a)), euint256.unwrap(s(b))));
108
+ }
109
+
110
+ function and(ebool a, ebool b) internal returns (ebool) {
111
+ return ebool.wrap(inco.eBitAnd(ebool.unwrap(s(a)), ebool.unwrap(s(b))));
112
+ }
113
+
114
+ function and(ebool a, bool b) internal returns (ebool) {
115
+ return ebool.wrap(inco.eBitAnd(ebool.unwrap(s(a)), ebool.unwrap(asEbool(b))));
116
+ }
117
+
118
+ function and(bool a, ebool b) internal returns (ebool) {
119
+ return ebool.wrap(inco.eBitAnd(ebool.unwrap(asEbool(a)), ebool.unwrap(s(b))));
120
+ }
121
+
122
+ function or(euint256 a, euint256 b) internal returns (euint256) {
123
+ return euint256.wrap(inco.eBitOr(euint256.unwrap(s(a)), euint256.unwrap(s(b))));
124
+ }
125
+
126
+ function or(euint256 a, uint256 b) internal returns (euint256) {
127
+ return euint256.wrap(inco.eBitOr(euint256.unwrap(s(a)), euint256.unwrap(asEuint256(b))));
128
+ }
129
+
130
+ function or(uint256 a, euint256 b) internal returns (euint256) {
131
+ return euint256.wrap(inco.eBitOr(euint256.unwrap(asEuint256(a)), euint256.unwrap(s(b))));
132
+ }
133
+
134
+ function or(ebool a, ebool b) internal returns (ebool) {
135
+ return ebool.wrap(inco.eBitOr(ebool.unwrap(s(a)), ebool.unwrap(s(b))));
136
+ }
137
+
138
+ function or(ebool a, bool b) internal returns (ebool) {
139
+ return ebool.wrap(inco.eBitOr(ebool.unwrap(s(a)), ebool.unwrap(asEbool(b))));
140
+ }
141
+
142
+ function or(bool a, ebool b) internal returns (ebool) {
143
+ return ebool.wrap(inco.eBitOr(ebool.unwrap(asEbool(a)), ebool.unwrap(s(b))));
144
+ }
145
+
146
+ function xor(euint256 a, euint256 b) internal returns (euint256) {
147
+ return euint256.wrap(inco.eBitXor(euint256.unwrap(s(a)), euint256.unwrap(s(b))));
148
+ }
149
+
150
+ function xor(euint256 a, uint256 b) internal returns (euint256) {
151
+ return euint256.wrap(inco.eBitXor(euint256.unwrap(s(a)), euint256.unwrap(asEuint256(b))));
152
+ }
153
+
154
+ function xor(uint256 a, euint256 b) internal returns (euint256) {
155
+ return euint256.wrap(inco.eBitXor(euint256.unwrap(asEuint256(a)), euint256.unwrap(s(b))));
156
+ }
157
+
158
+ function xor(ebool a, ebool b) internal returns (ebool) {
159
+ return ebool.wrap(inco.eBitXor(ebool.unwrap(s(a)), ebool.unwrap(s(b))));
160
+ }
161
+
162
+ function xor(ebool a, bool b) internal returns (ebool) {
163
+ return ebool.wrap(inco.eBitXor(ebool.unwrap(s(a)), ebool.unwrap(asEbool(b))));
164
+ }
165
+
166
+ function xor(bool a, ebool b) internal returns (ebool) {
167
+ return ebool.wrap(inco.eBitXor(ebool.unwrap(asEbool(a)), ebool.unwrap(s(b))));
168
+ }
169
+
170
+ function shl(euint256 a, euint256 b) internal returns (euint256) {
171
+ return inco.eShl(s(a), s(b));
172
+ }
173
+
174
+ function shl(euint256 a, uint256 b) internal returns (euint256) {
175
+ return inco.eShl(s(a), asEuint256(b));
176
+ }
177
+
178
+ function shl(uint256 a, euint256 b) internal returns (euint256) {
179
+ return inco.eShl(asEuint256(a), s(b));
180
+ }
181
+
182
+ function shr(euint256 a, euint256 b) internal returns (euint256) {
183
+ return inco.eShr(s(a), s(b));
184
+ }
185
+
186
+ function shr(euint256 a, uint256 b) internal returns (euint256) {
187
+ return inco.eShr(s(a), asEuint256(b));
188
+ }
189
+
190
+ function shr(uint256 a, euint256 b) internal returns (euint256) {
191
+ return inco.eShr(asEuint256(a), s(b));
192
+ }
193
+
194
+ function rotl(euint256 a, euint256 b) internal returns (euint256) {
195
+ return inco.eRotl(s(a), s(b));
196
+ }
197
+
198
+ function rotl(euint256 a, uint256 b) internal returns (euint256) {
199
+ return inco.eRotl(s(a), asEuint256(b));
200
+ }
201
+
202
+ function rotl(uint256 a, euint256 b) internal returns (euint256) {
203
+ return inco.eRotl(asEuint256(a), s(b));
204
+ }
205
+
206
+ function rotr(euint256 a, euint256 b) internal returns (euint256) {
207
+ return inco.eRotr(s(a), s(b));
208
+ }
209
+
210
+ function rotr(euint256 a, uint256 b) internal returns (euint256) {
211
+ return inco.eRotr(s(a), asEuint256(b));
212
+ }
213
+
214
+ function rotr(uint256 a, euint256 b) internal returns (euint256) {
215
+ return inco.eRotr(asEuint256(a), s(b));
216
+ }
217
+
218
+ function eq(euint256 a, euint256 b) internal returns (ebool) {
219
+ return inco.eEq(s(a), s(b));
220
+ }
221
+
222
+ function eq(euint256 a, uint256 b) internal returns (ebool) {
223
+ return inco.eEq(s(a), asEuint256(b));
224
+ }
225
+
226
+ function eq(uint256 a, euint256 b) internal returns (ebool) {
227
+ return inco.eEq(asEuint256(a), s(b));
228
+ }
229
+
230
+ function ne(euint256 a, euint256 b) internal returns (ebool) {
231
+ return inco.eNe(s(a), s(b));
232
+ }
233
+
234
+ function ne(euint256 a, uint256 b) internal returns (ebool) {
235
+ return inco.eNe(s(a), asEuint256(b));
236
+ }
237
+
238
+ function ne(uint256 a, euint256 b) internal returns (ebool) {
239
+ return inco.eNe(asEuint256(a), s(b));
240
+ }
241
+
242
+ function ge(euint256 a, euint256 b) internal returns (ebool) {
243
+ return inco.eGe(s(a), s(b));
244
+ }
245
+
246
+ function ge(euint256 a, uint256 b) internal returns (ebool) {
247
+ return inco.eGe(s(a), asEuint256(b));
248
+ }
249
+
250
+ function ge(uint256 a, euint256 b) internal returns (ebool) {
251
+ return inco.eGe(asEuint256(a), s(b));
252
+ }
253
+
254
+ function gt(euint256 a, euint256 b) internal returns (ebool) {
255
+ return inco.eGt(s(a), s(b));
256
+ }
257
+
258
+ function gt(euint256 a, uint256 b) internal returns (ebool) {
259
+ return inco.eGt(s(a), asEuint256(b));
260
+ }
261
+
262
+ function gt(uint256 a, euint256 b) internal returns (ebool) {
263
+ return inco.eGt(asEuint256(a), s(b));
264
+ }
265
+
266
+ function le(euint256 a, euint256 b) internal returns (ebool) {
267
+ return inco.eLe(s(a), s(b));
268
+ }
269
+
270
+ function le(euint256 a, uint256 b) internal returns (ebool) {
271
+ return inco.eLe(s(a), asEuint256(b));
272
+ }
273
+
274
+ function le(uint256 a, euint256 b) internal returns (ebool) {
275
+ return inco.eLe(asEuint256(a), s(b));
276
+ }
277
+
278
+ function lt(euint256 a, euint256 b) internal returns (ebool) {
279
+ return inco.eLt(s(a), s(b));
280
+ }
281
+
282
+ function lt(euint256 a, uint256 b) internal returns (ebool) {
283
+ return inco.eLt(s(a), asEuint256(b));
284
+ }
285
+
286
+ function lt(uint256 a, euint256 b) internal returns (ebool) {
287
+ return inco.eLt(asEuint256(a), s(b));
288
+ }
289
+
290
+ function min(euint256 a, euint256 b) internal returns (euint256) {
291
+ return inco.eMin(s(a), s(b));
292
+ }
293
+
294
+ function min(euint256 a, uint256 b) internal returns (euint256) {
295
+ return inco.eMin(s(a), asEuint256(b));
296
+ }
297
+
298
+ function min(uint256 a, euint256 b) internal returns (euint256) {
299
+ return inco.eMin(asEuint256(a), s(b));
300
+ }
301
+
302
+ function max(euint256 a, euint256 b) internal returns (euint256) {
303
+ return inco.eMax(s(a), s(b));
304
+ }
305
+
306
+ function max(euint256 a, uint256 b) internal returns (euint256) {
307
+ return inco.eMax(s(a), asEuint256(b));
308
+ }
309
+
310
+ function max(uint256 a, euint256 b) internal returns (euint256) {
311
+ return inco.eMax(asEuint256(a), s(b));
312
+ }
313
+
314
+ function not(ebool a) internal returns (ebool) {
315
+ return inco.eNot(s(a));
316
+ }
317
+
318
+ function rand() internal returns (euint256) {
319
+ return euint256.wrap(inco.eRand(ETypes.Uint256));
320
+ }
321
+
322
+ function randBounded(uint256 upperBound) internal returns (euint256) {
323
+ return euint256.wrap(inco.eRandBounded(euint256.unwrap(asEuint256(upperBound)), ETypes.Uint256));
324
+ }
325
+
326
+ function randBounded(euint256 upperBound) internal returns (euint256) {
327
+ return euint256.wrap(inco.eRandBounded(euint256.unwrap(s(upperBound)), ETypes.Uint256));
328
+ }
329
+
330
+ function asEuint256(uint256 a) internal returns (euint256) {
331
+ return inco.asEuint256(a);
332
+ }
333
+
334
+ function asEbool(bool a) internal returns (ebool) {
335
+ return inco.asEbool(a);
336
+ }
337
+
338
+ function asEbool(euint256 a) internal returns (ebool) {
339
+ return ebool.wrap(inco.eCast(euint256.unwrap(a), ETypes.Bool));
340
+ }
341
+
342
+ function asEuint256(ebool a) internal returns (euint256) {
343
+ return euint256.wrap(inco.eCast(ebool.unwrap(a), ETypes.Uint256));
344
+ }
345
+
346
+ function newEuint256(bytes memory ciphertext, address user) internal returns (euint256) {
347
+ return inco.newEuint256(ciphertext, user);
348
+ }
349
+
350
+ function newEbool(bytes memory ciphertext, address user) internal returns (ebool) {
351
+ return inco.newEbool(ciphertext, user);
352
+ }
353
+
354
+ function allow(euint256 a, address to) internal {
355
+ inco.allow(euint256.unwrap(a), to);
356
+ }
357
+
358
+ function allow(ebool a, address to) internal {
359
+ inco.allow(ebool.unwrap(a), to);
360
+ }
361
+
362
+ function allowThis(euint256 a) internal {
363
+ allow(a, address(this));
364
+ }
365
+
366
+ function allowThis(ebool a) internal {
367
+ allow(a, address(this));
368
+ }
369
+
370
+ function isAllowed(address user, euint256 a) internal view returns (bool) {
371
+ return inco.isAllowed(euint256.unwrap(a), user);
372
+ }
373
+
374
+ function select(ebool control, euint256 ifTrue, euint256 ifFalse) internal returns (euint256) {
375
+ return euint256.wrap(inco.eIfThenElse(s(control), euint256.unwrap(s(ifTrue)), euint256.unwrap(s(ifFalse))));
376
+ }
377
+
378
+ function select(ebool control, ebool ifTrue, ebool ifFalse) internal returns (ebool) {
379
+ return ebool.wrap(inco.eIfThenElse(s(control), ebool.unwrap(s(ifTrue)), ebool.unwrap(s(ifFalse))));
380
+ }
381
+
382
+ function requestDecryption(euint256 a, bytes4 callbackSelector, bytes memory callbackData) internal returns (uint256 requestId) {
383
+ requestId = inco.requestDecryption(callbackSelector, block.timestamp + defaultDecryptionDelayLimit, euint256.unwrap(s(a)), callbackData);
384
+ }
385
+
386
+ function requestDecryption(ebool a, bytes4 callbackSelector, bytes memory callbackData) internal returns (uint256 requestId) {
387
+ requestId = inco.requestDecryption(callbackSelector, block.timestamp + defaultDecryptionDelayLimit, ebool.unwrap(s(a)), callbackData);
388
+ }
389
+ }
@@ -1,24 +1,35 @@
1
1
  // SPDX-License-Identifier: No License
2
2
  pragma solidity ^0.8;
3
3
 
4
- import {BaseAccessControlList} from "./AccessControl/BaseAccessControlList.sol";
5
4
  import {SenderNotAllowedForHandle} from "../Types.sol";
6
- import {SignatureVerifier} from "./primitives/SignatureVerifier.sol";
5
+ import {BaseAccessControlList} from "./AccessControl/BaseAccessControlList.sol";
7
6
  import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
8
7
  import {EventCounter} from "./primitives/EventCounter.sol";
8
+ import {SignatureVerifier} from "./primitives/SignatureVerifier.sol";
9
9
 
10
10
  struct Request {
11
11
  uint256 maxTimestamp;
12
12
  address callbackContract;
13
13
  bytes4 callbackSelector;
14
- // todo #450 should contain the handle
15
14
  bool fulfilled;
16
15
  bytes data;
16
+ // Adding at end of struct which should be unused state slot for version 0.2.0
17
+ bytes32 handle;
18
+ // This value is redundant for the contract but required for getPendingRequests, rather than duplicate this struct
19
+ // adding it here redundantly for now
20
+ uint256 requestId;
17
21
  }
18
22
 
19
23
  struct DecryptionResult {
20
24
  bytes32 abiEncodedResult;
21
25
  uint256 requestId;
26
+ // Added in 0.2.0 to offer an integrity check
27
+ bytes32 handle;
28
+ }
29
+
30
+ struct SignedDecryptionResult {
31
+ DecryptionResult result;
32
+ bytes signature;
22
33
  }
23
34
 
24
35
  event DecryptionRequested(
@@ -30,8 +41,13 @@ event DecryptionRequested(
30
41
 
31
42
  contract DecryptionHandlerStorage {
32
43
  struct DecryptionStorage {
33
- uint256 requestIdCounter;
34
- mapping(uint256 => Request) request;
44
+ // The next request ID to assign. This and higher numbers have never been used,
45
+ // hence there are no pending requests in that range.
46
+ uint256 nextRequestId;
47
+ mapping(uint256 => Request) requests;
48
+ // A requestId for which all previous requests have been fulfilled or are expired.
49
+ // Must be less than or equal to nextRequestId. Only increases.
50
+ uint256 lowWatermark;
35
51
  }
36
52
 
37
53
  bytes32 private constant DecryptionStorageLocation =
@@ -85,16 +101,24 @@ abstract contract DecryptionHandler is
85
101
  DecryptionHandlerEip712Checker
86
102
  {
87
103
  // The Covalidator callback machinery relies on these exact error signatures which are hardcoded in eth_callback.go
88
- error RequestAlreadyFulfilled();
89
- error RequestExpired();
90
- error InvalidResultSignature();
104
+ error AllRequestsAlreadyFulfilledOrExpired();
105
+ // Thrown when either msg.sender is not a valid signer or the signature is not from a valid signer
106
+ error InvalidResultSignature(address msgSender, bytes signature);
91
107
 
92
108
  event RequestFulfilled(
93
109
  uint256 indexed requestId,
94
110
  bool success,
95
- uint256 eventId
111
+ uint256 eventId,
112
+ bytes32 handle
96
113
  );
97
114
 
115
+ // Maximum number of request mappings to access on any operation.
116
+ uint256 constant ITERATION_BOUND = 100;
117
+
118
+ function pending(Request memory r) internal view returns (bool) {
119
+ return !r.fulfilled && r.maxTimestamp >= block.timestamp;
120
+ }
121
+
98
122
  /// @dev callback function MUST be of form `function name(bytes32 requestId, bytes32 result, bytes memory data)`
99
123
  function requestDecryption(
100
124
  // todo support multiple handles per request
@@ -109,15 +133,20 @@ abstract contract DecryptionHandler is
109
133
  );
110
134
 
111
135
  DecryptionStorage storage $ = getDecryptionStorage();
112
- requestId = $.requestIdCounter++;
113
- $.request[requestId] = Request({
136
+ requestId = $.nextRequestId;
137
+ $.nextRequestId++;
138
+ $.requests[requestId] = Request({
114
139
  maxTimestamp: maxTimestamp,
115
140
  callbackContract: msg.sender,
116
141
  callbackSelector: callbackSelector,
117
142
  fulfilled: false,
118
- data: data
143
+ data: data,
144
+ handle: handle,
145
+ requestId: requestId
119
146
  });
120
147
 
148
+ advanceLowWatermark(ITERATION_BOUND);
149
+
121
150
  emit DecryptionRequested(
122
151
  requestId,
123
152
  handle,
@@ -128,37 +157,179 @@ abstract contract DecryptionHandler is
128
157
 
129
158
  function fulfillRequest(
130
159
  DecryptionResult memory result,
131
- bytes memory /* signature */
160
+ bytes memory signature
132
161
  ) external {
162
+ SignedDecryptionResult[]
163
+ memory signedResults = new SignedDecryptionResult[](1);
164
+ signedResults[0] = SignedDecryptionResult({
165
+ result: result,
166
+ signature: signature
167
+ });
168
+ fulfillRequestsBatch(signedResults);
169
+ }
170
+
171
+ function fulfillRequestsBatch(
172
+ SignedDecryptionResult[] memory results
173
+ ) public {
133
174
  DecryptionStorage storage $ = getDecryptionStorage();
134
- Request storage request = $.request[result.requestId];
135
175
 
136
- require(!request.fulfilled, RequestAlreadyFulfilled());
137
- require(request.maxTimestamp >= block.timestamp, RequestExpired());
138
- // require(isValidResult(result, signature), InvalidResultSignature());
139
- require(
140
- msg.sender == opSignerPubkeyAddress(),
141
- InvalidResultSignature()
142
- ); // todo beta implem so backend does not need to implement EIP712 signing yet
143
- request.fulfilled = true;
144
-
145
- bytes memory callbackCalldata = abi.encodeWithSelector(
146
- request.callbackSelector,
147
- result.requestId,
148
- result.abiEncodedResult,
149
- request.data
150
- );
151
- (bool success, ) = request.callbackContract.call(callbackCalldata);
152
- emit RequestFulfilled(result.requestId, success, getNewEventId());
176
+ bool fulfilledSome = false;
177
+
178
+ for (uint i = 0; i < results.length; i++) {
179
+ Request storage request = $.requests[results[i].result.requestId];
180
+
181
+ if (!pending(request)) {
182
+ // The request was already fulfilled, or has expired. Neither is an error.
183
+ continue;
184
+ }
185
+ // Support either an EIP712 signature or a direct call from the signer
186
+ require(
187
+ isSigner(msg.sender) ||
188
+ isValidResult(results[i].result, results[i].signature),
189
+ InvalidResultSignature(msg.sender, results[i].signature)
190
+ );
191
+
192
+ bytes memory callbackCalldata = abi.encodeWithSelector(
193
+ request.callbackSelector,
194
+ results[i].result.requestId,
195
+ results[i].result.abiEncodedResult,
196
+ request.data
197
+ // TODO: add handle as an integrity check - already added to DecryptionResult but adding here would mean
198
+ // dapp callback function signatures would need to change - which would be a breaking change
199
+ // request.handle
200
+ );
201
+
202
+ (bool success, ) = request.callbackContract.call(callbackCalldata);
203
+
204
+ request.fulfilled = true;
205
+ fulfilledSome = true;
206
+
207
+ emit RequestFulfilled(
208
+ results[i].result.requestId,
209
+ success,
210
+ getNewEventId(),
211
+ results[i].result.handle
212
+ );
213
+ }
214
+ // If we did not advance we revert, so that we can detect if everything is fulfilled during gas estimation
215
+ require(fulfilledSome, AllRequestsAlreadyFulfilledOrExpired());
216
+
217
+ // Otherwise, do some work to advance the low water mark.
218
+ advanceLowWatermark(ITERATION_BOUND);
153
219
  }
154
220
 
155
- function getRequest(
156
- uint256 requestId
157
- ) external view returns (Request memory) {
158
- return getDecryptionStorage().request[requestId];
221
+ /**
222
+ * @dev Get some pending requests.
223
+ * @param offset A parameter for paginating through the whole set of pending requests.
224
+ * Use zero for an initial query, then use the previous query's nextOffset to continue traversal.
225
+ * @param maxRequests The maximum number of pending to return.
226
+ * @param iterationBound The maximum number of non-pending requests to iterate over before returning.
227
+ * @return nextOffset The next offset to use to continue to scan through the pending request set.
228
+ * Returns the input offset if the whole set has been scanned. Can re-query with this offset
229
+ * to see any new pending requests, or re-query with a zero offset to return the whole set again.
230
+
231
+ * @return requests Some pending requests. May be fewer than requested, possibly zero. There may be
232
+ * more requests available if the returned nextOffset is greater than the input offset.
233
+ */
234
+ function getPendingRequests(
235
+ uint256 offset,
236
+ uint256 maxRequests,
237
+ uint256 iterationBound
238
+ ) external view returns (uint256 nextOffset, Request[] memory requests) {
239
+ DecryptionStorage storage $ = getDecryptionStorage();
240
+
241
+ uint256 lwm = $.lowWatermark;
242
+ uint256 nextRequestId = $.nextRequestId;
243
+
244
+ // Start at the higher of offset and lwm, possibly skipping many completed requests.
245
+ uint256 id = offset;
246
+ if (id < lwm) {
247
+ id = lwm;
248
+ }
249
+
250
+ // Don't scan more than iterationBound extra entries or pass the nextRequestId.
251
+ uint256 limit = id + maxRequests + iterationBound;
252
+ if (limit > nextRequestId) {
253
+ limit = nextRequestId;
254
+ }
255
+
256
+ // Now id >= lmw and limit <= nextRequestId.
257
+ // It's possible that id >= limit, which is fine,
258
+ // and the next two loops will exit immediately.
259
+
260
+ // First calculate the size of array we need to allocate.
261
+ // (It's cheaper to scan the requests twice instead of allocating too much.)
262
+ uint256 numRequests = 0;
263
+ for (uint256 i = id; i < limit && numRequests < maxRequests; i++) {
264
+ Request memory request = $.requests[i];
265
+ if (pending(request)) {
266
+ numRequests++;
267
+ }
268
+ }
269
+
270
+ // Now populate the array
271
+ requests = new Request[](numRequests);
272
+ for (uint256 n = 0; id < limit && n < numRequests; id++) {
273
+ Request memory request = $.requests[id];
274
+ if (pending(request)) {
275
+ requests[n] = request;
276
+ n++;
277
+ }
278
+ }
279
+ nextOffset = id;
280
+ }
281
+
282
+ /**
283
+ * @dev Get some pending requests with a bounded number of iterations.
284
+ * @param offset A parameter for paginating through the whole set of pending requests.
285
+ * Use zero for an initial query, then use the previous query's nextOffset to continue traversal.
286
+ * @param maxRequests The maximum number of pending to return.
287
+ * @return nextOffset The next offset to use to continue to scan through the pending request set.
288
+ * Returns the input offset if the whole set has been scanned. Can re-query with this offset
289
+ * to see any new pending requests, or re-query with a zero offset to return the whole set again.
290
+ * @return requests Some pending requests. May be fewer than requested, possibly zero. There may be
291
+ * more requests available if the returned nextOffset is greater than the input offset.
292
+ */
293
+ function getPendingRequests(
294
+ uint256 offset,
295
+ uint256 maxRequests
296
+ ) external view returns (uint256 nextOffset, Request[] memory requests) {
297
+ return this.getPendingRequests(offset, maxRequests, ITERATION_BOUND);
298
+ }
299
+
300
+ function getNextRequestId() external view returns (uint256) {
301
+ return getDecryptionStorage().nextRequestId;
159
302
  }
160
303
 
161
- function getRequestIdCounter() external view returns (uint256) {
162
- return getDecryptionStorage().requestIdCounter;
304
+ function getLowWatermarkRequestId() external view returns (uint256) {
305
+ return getDecryptionStorage().lowWatermark;
306
+ }
307
+
308
+ /**
309
+ * @dev Advance the low watermark until we hit a pending request, the requestId counter, or iterationBound steps.
310
+ * @param iterationBound The maximum number of requests to iterate over.
311
+ * @notice This function is public so that we can externally drive the low watermark in extreme cases.
312
+ */
313
+ function advanceLowWatermark(uint256 iterationBound) public {
314
+ DecryptionStorage storage $ = getDecryptionStorage();
315
+ uint256 lwm = $.lowWatermark;
316
+ uint256 nextRequestId = $.nextRequestId;
317
+ // Calling advanceLowWatermark with a 0 iterationBound will have no effect, but it will waste gas, and previously
318
+ // has been indicative of a bug, so leaving this revert as a affordance to callers.
319
+ require(iterationBound > 0, "iterateBound is zero!");
320
+ uint256 limit = lwm + iterationBound;
321
+ if (limit > nextRequestId) {
322
+ limit = nextRequestId;
323
+ }
324
+
325
+ while (lwm < limit) {
326
+ Request storage request = $.requests[lwm];
327
+ // Stop advancing when we hit the first still pending request
328
+ if (pending(request)) {
329
+ break;
330
+ }
331
+ lwm++;
332
+ }
333
+ $.lowWatermark = lwm;
163
334
  }
164
335
  }