@@ -76,7 +76,7 @@ contract RecurringCollectorUpdateTest is RecurringCollectorSharedTest {
76
76
);
77
77
rcau.agreementId = accepted.rca.agreementId;
78
78
79
- IRecurringCollector.SignedRCAU memory signedRCAU = _recurringCollectorHelper.generateSignedRCAU (
79
+ IRecurringCollector.SignedRCAU memory signedRCAU = _recurringCollectorHelper.generateSignedRCAUWithCorrectNonce (
80
80
rcau,
81
81
signerKey
82
82
);
@@ -124,6 +124,8 @@ contract RecurringCollectorUpdateTest is RecurringCollectorSharedTest {
124
124
fuzzyTestUpdate.rcau
125
125
);
126
126
rcau.agreementId = accepted.rca.agreementId;
127
+ // Don't use fuzzed nonce - use correct nonce for first update
128
+ rcau.nonce = 1 ;
127
129
IRecurringCollector.SignedRCAU memory signedRCAU = _recurringCollectorHelper.generateSignedRCAU (
128
130
rcau,
129
131
signerKey
@@ -151,6 +153,151 @@ contract RecurringCollectorUpdateTest is RecurringCollectorSharedTest {
151
153
assertEq (rcau.maxOngoingTokensPerSecond, agreement.maxOngoingTokensPerSecond);
152
154
assertEq (rcau.minSecondsPerCollection, agreement.minSecondsPerCollection);
153
155
assertEq (rcau.maxSecondsPerCollection, agreement.maxSecondsPerCollection);
156
+ assertEq (rcau.nonce, agreement.updateNonce);
157
+ }
158
+
159
+ function test_Update_Revert_WhenInvalidNonce_TooLow (FuzzyTestUpdate calldata fuzzyTestUpdate ) public {
160
+ (IRecurringCollector.SignedRCA memory accepted , uint256 signerKey ) = _sensibleAuthorizeAndAccept (
161
+ fuzzyTestUpdate.fuzzyTestAccept
162
+ );
163
+ IRecurringCollector.RecurringCollectionAgreementUpdate memory rcau = _recurringCollectorHelper.sensibleRCAU (
164
+ fuzzyTestUpdate.rcau
165
+ );
166
+ rcau.agreementId = accepted.rca.agreementId;
167
+ rcau.nonce = 0 ; // Invalid: should be 1 for first update
168
+
169
+ IRecurringCollector.SignedRCAU memory signedRCAU = _recurringCollectorHelper.generateSignedRCAU (
170
+ rcau,
171
+ signerKey
172
+ );
173
+
174
+ bytes memory expectedErr = abi.encodeWithSelector (
175
+ IRecurringCollector.RecurringCollectorInvalidUpdateNonce.selector ,
176
+ rcau.agreementId,
177
+ 1 , // expected
178
+ 0 // provided
179
+ );
180
+ vm.expectRevert (expectedErr);
181
+ vm.prank (accepted.rca.dataService);
182
+ _recurringCollector.update (signedRCAU);
183
+ }
184
+
185
+ function test_Update_Revert_WhenInvalidNonce_TooHigh (FuzzyTestUpdate calldata fuzzyTestUpdate ) public {
186
+ (IRecurringCollector.SignedRCA memory accepted , uint256 signerKey ) = _sensibleAuthorizeAndAccept (
187
+ fuzzyTestUpdate.fuzzyTestAccept
188
+ );
189
+ IRecurringCollector.RecurringCollectionAgreementUpdate memory rcau = _recurringCollectorHelper.sensibleRCAU (
190
+ fuzzyTestUpdate.rcau
191
+ );
192
+ rcau.agreementId = accepted.rca.agreementId;
193
+ rcau.nonce = 5 ; // Invalid: should be 1 for first update
194
+
195
+ IRecurringCollector.SignedRCAU memory signedRCAU = _recurringCollectorHelper.generateSignedRCAU (
196
+ rcau,
197
+ signerKey
198
+ );
199
+
200
+ bytes memory expectedErr = abi.encodeWithSelector (
201
+ IRecurringCollector.RecurringCollectorInvalidUpdateNonce.selector ,
202
+ rcau.agreementId,
203
+ 1 , // expected
204
+ 5 // provided
205
+ );
206
+ vm.expectRevert (expectedErr);
207
+ vm.prank (accepted.rca.dataService);
208
+ _recurringCollector.update (signedRCAU);
209
+ }
210
+
211
+ function test_Update_Revert_WhenReplayAttack (FuzzyTestUpdate calldata fuzzyTestUpdate ) public {
212
+ (IRecurringCollector.SignedRCA memory accepted , uint256 signerKey ) = _sensibleAuthorizeAndAccept (
213
+ fuzzyTestUpdate.fuzzyTestAccept
214
+ );
215
+ IRecurringCollector.RecurringCollectionAgreementUpdate memory rcau1 = _recurringCollectorHelper.sensibleRCAU (
216
+ fuzzyTestUpdate.rcau
217
+ );
218
+ rcau1.agreementId = accepted.rca.agreementId;
219
+ rcau1.nonce = 1 ;
220
+
221
+ // First update succeeds
222
+ IRecurringCollector.SignedRCAU memory signedRCAU1 = _recurringCollectorHelper.generateSignedRCAU (
223
+ rcau1,
224
+ signerKey
225
+ );
226
+ vm.prank (accepted.rca.dataService);
227
+ _recurringCollector.update (signedRCAU1);
228
+
229
+ // Second update with different terms and nonce 2 succeeds
230
+ IRecurringCollector.RecurringCollectionAgreementUpdate memory rcau2 = rcau1;
231
+ rcau2.nonce = 2 ;
232
+ rcau2.maxOngoingTokensPerSecond = rcau1.maxOngoingTokensPerSecond * 2 ; // Different terms
233
+
234
+ IRecurringCollector.SignedRCAU memory signedRCAU2 = _recurringCollectorHelper.generateSignedRCAU (
235
+ rcau2,
236
+ signerKey
237
+ );
238
+ vm.prank (accepted.rca.dataService);
239
+ _recurringCollector.update (signedRCAU2);
240
+
241
+ // Attempting to replay first update should fail
242
+ bytes memory expectedErr = abi.encodeWithSelector (
243
+ IRecurringCollector.RecurringCollectorInvalidUpdateNonce.selector ,
244
+ rcau1.agreementId,
245
+ 3 , // expected (current nonce + 1)
246
+ 1 // provided (old nonce)
247
+ );
248
+ vm.expectRevert (expectedErr);
249
+ vm.prank (accepted.rca.dataService);
250
+ _recurringCollector.update (signedRCAU1);
251
+ }
252
+
253
+ function test_Update_OK_NonceIncrementsCorrectly (FuzzyTestUpdate calldata fuzzyTestUpdate ) public {
254
+ (IRecurringCollector.SignedRCA memory accepted , uint256 signerKey ) = _sensibleAuthorizeAndAccept (
255
+ fuzzyTestUpdate.fuzzyTestAccept
256
+ );
257
+
258
+ // Initial nonce should be 0
259
+ IRecurringCollector.AgreementData memory initialAgreement = _recurringCollector.getAgreement (
260
+ accepted.rca.agreementId
261
+ );
262
+ assertEq (initialAgreement.updateNonce, 0 );
263
+
264
+ // First update with nonce 1
265
+ IRecurringCollector.RecurringCollectionAgreementUpdate memory rcau1 = _recurringCollectorHelper.sensibleRCAU (
266
+ fuzzyTestUpdate.rcau
267
+ );
268
+ rcau1.agreementId = accepted.rca.agreementId;
269
+ rcau1.nonce = 1 ;
270
+
271
+ IRecurringCollector.SignedRCAU memory signedRCAU1 = _recurringCollectorHelper.generateSignedRCAU (
272
+ rcau1,
273
+ signerKey
274
+ );
275
+ vm.prank (accepted.rca.dataService);
276
+ _recurringCollector.update (signedRCAU1);
277
+
278
+ // Verify nonce incremented to 1
279
+ IRecurringCollector.AgreementData memory updatedAgreement1 = _recurringCollector.getAgreement (
280
+ accepted.rca.agreementId
281
+ );
282
+ assertEq (updatedAgreement1.updateNonce, 1 );
283
+
284
+ // Second update with nonce 2
285
+ IRecurringCollector.RecurringCollectionAgreementUpdate memory rcau2 = rcau1;
286
+ rcau2.nonce = 2 ;
287
+ rcau2.maxOngoingTokensPerSecond = rcau1.maxOngoingTokensPerSecond * 2 ; // Different terms
288
+
289
+ IRecurringCollector.SignedRCAU memory signedRCAU2 = _recurringCollectorHelper.generateSignedRCAU (
290
+ rcau2,
291
+ signerKey
292
+ );
293
+ vm.prank (accepted.rca.dataService);
294
+ _recurringCollector.update (signedRCAU2);
295
+
296
+ // Verify nonce incremented to 2
297
+ IRecurringCollector.AgreementData memory updatedAgreement2 = _recurringCollector.getAgreement (
298
+ accepted.rca.agreementId
299
+ );
300
+ assertEq (updatedAgreement2.updateNonce, 2 );
154
301
}
155
302
156
303
/* solhint-enable graph/func-name-mixedcase */
0 commit comments