Skip to content

Commit e7c609e

Browse files
authored
feat(IValidator): Allow Forward Progress Despite Missing Proofs (#321)
Reviewer @rvagg It is only important to block forward progress through the current proving period. Currently we don't allow forward progress through unsettled periods without proofs. This could be a problem if there was a rail unproven for a long time but proofs were later resumed. Though much less likely after #258 and #267, we would not want it to be possible for the gap to exceed the block gas limit. Also important to recall that the `fromEpoch` is an exclusive index while `toEpoch` is an inclusive index. #### Changes * allow to settleUpTo previous period when unproven * define settledUpTo in previously irrelevant case
1 parent 8399813 commit e7c609e

File tree

2 files changed

+63
-41
lines changed

2 files changed

+63
-41
lines changed

service_contracts/src/FilecoinWarmStorageService.sol

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,6 @@ contract FilecoinWarmStorageService is
876876
revert Errors.InvalidChallengeEpoch(dataSetId, minWindow, maxWindow, challengeEpoch);
877877
}
878878
provingDeadlines[dataSetId] = firstDeadline;
879-
provenThisPeriod[dataSetId] = false;
880879

881880
// Initialize the activation epoch when proving first starts
882881
// This marks when the data set became active for proving
@@ -926,15 +925,6 @@ contract FilecoinWarmStorageService is
926925
emit FaultRecord(dataSetId, faultPeriods, provingDeadlines[dataSetId]);
927926
}
928927

929-
// Record the status of the current/previous proving period that's ending
930-
if (provingDeadlines[dataSetId] != NO_PROVING_DEADLINE && provenThisPeriod[dataSetId]) {
931-
// Determine the period ID that just completed
932-
uint256 completedPeriodId = getProvingPeriodForEpoch(dataSetId, provingDeadlines[dataSetId] - 1);
933-
934-
// Record whether this period was proven
935-
provenPeriods[dataSetId][completedPeriodId >> 8] |= 1 << (completedPeriodId & 255);
936-
}
937-
938928
provingDeadlines[dataSetId] = nextDeadline;
939929
provenThisPeriod[dataSetId] = false;
940930

@@ -1179,8 +1169,10 @@ contract FilecoinWarmStorageService is
11791169
* @return The period ID this epoch belongs to, or type(uint256).max if before activation
11801170
*/
11811171
function getProvingPeriodForEpoch(uint256 dataSetId, uint256 epoch) public view returns (uint256) {
1182-
uint256 activationEpoch = provingActivationEpoch[dataSetId];
1172+
return _provingPeriodForEpoch(provingActivationEpoch[dataSetId], epoch);
1173+
}
11831174

1175+
function _provingPeriodForEpoch(uint256 activationEpoch, uint256 epoch) internal view returns (uint256) {
11841176
// If proving wasn't activated or epoch is before activation
11851177
if (activationEpoch == 0 || epoch < activationEpoch) {
11861178
return type(uint256).max; // Invalid period
@@ -1484,78 +1476,71 @@ contract FilecoinWarmStorageService is
14841476
});
14851477
}
14861478

1487-
// Count proven epochs and find the last proven epoch
1488-
(uint256 provenEpochCount, uint256 lastProvenEpoch) =
1479+
// Count proven epochs up to toEpoch, possibly stopping earlier if unresolved
1480+
(uint256 provenEpochCount, uint256 settleUpTo) =
14891481
_findProvenEpochs(dataSetId, fromEpoch, toEpoch, activationEpoch);
14901482

14911483
// If no epochs are proven, we can't settle anything
14921484
if (provenEpochCount == 0) {
14931485
return ValidationResult({
14941486
modifiedAmount: 0,
1495-
settleUpto: fromEpoch,
1487+
settleUpto: settleUpTo,
14961488
note: "No proven epochs in the requested range"
14971489
});
14981490
}
14991491

15001492
// Calculate the modified amount based on proven epochs
15011493
uint256 modifiedAmount = (proposedAmount * provenEpochCount) / totalEpochsRequested;
15021494

1503-
return ValidationResult({
1504-
modifiedAmount: modifiedAmount,
1505-
settleUpto: lastProvenEpoch, // Settle up to the last proven epoch
1506-
note: ""
1507-
});
1495+
return ValidationResult({modifiedAmount: modifiedAmount, settleUpto: settleUpTo, note: ""});
15081496
}
15091497

15101498
function _findProvenEpochs(uint256 dataSetId, uint256 fromEpoch, uint256 toEpoch, uint256 activationEpoch)
15111499
internal
15121500
view
1513-
returns (uint256 provenEpochCount, uint256 settledUpTo)
1501+
returns (uint256 provenEpochCount, uint256 settleUpTo)
15141502
{
15151503
require(toEpoch >= activationEpoch && toEpoch <= block.number, Errors.InvalidEpochRange(fromEpoch, toEpoch));
1516-
uint256 currentPeriod = getProvingPeriodForEpoch(dataSetId, block.number);
1517-
15181504
if (fromEpoch < activationEpoch - 1) {
15191505
fromEpoch = activationEpoch - 1;
15201506
}
15211507

1522-
uint256 startingPeriod = getProvingPeriodForEpoch(dataSetId, fromEpoch + 1);
1508+
uint256 startingPeriod = _provingPeriodForEpoch(activationEpoch, fromEpoch + 1);
15231509

1524-
// handle first period separately
1510+
// handle first period separately; it may be partially settled already
15251511
uint256 startingPeriodDeadline = _calcPeriodDeadline(activationEpoch, startingPeriod);
15261512

15271513
if (toEpoch < startingPeriodDeadline) {
1528-
if (_isPeriodProven(dataSetId, startingPeriod, currentPeriod)) {
1514+
if (_isPeriodProven(dataSetId, startingPeriod)) {
15291515
provenEpochCount = toEpoch - fromEpoch;
1530-
settledUpTo = toEpoch;
1516+
settleUpTo = toEpoch;
1517+
} else {
1518+
settleUpTo = fromEpoch;
15311519
}
15321520
} else {
1533-
if (_isPeriodProven(dataSetId, startingPeriod, currentPeriod)) {
1521+
if (_isPeriodProven(dataSetId, startingPeriod)) {
15341522
provenEpochCount += (startingPeriodDeadline - fromEpoch);
15351523
}
15361524

1537-
uint256 endingPeriod = getProvingPeriodForEpoch(dataSetId, toEpoch);
1525+
uint256 endingPeriod = _provingPeriodForEpoch(activationEpoch, toEpoch);
15381526
// loop through the proving periods between startingPeriod and endingPeriod
15391527
for (uint256 period = startingPeriod + 1; period < endingPeriod; period++) {
1540-
if (_isPeriodProven(dataSetId, period, currentPeriod)) {
1528+
if (_isPeriodProven(dataSetId, period)) {
15411529
provenEpochCount += maxProvingPeriod;
15421530
}
15431531
}
1544-
settledUpTo = _calcPeriodDeadline(activationEpoch, endingPeriod - 1);
1532+
settleUpTo = _calcPeriodDeadline(activationEpoch, endingPeriod - 1);
15451533

15461534
// handle the last period separately
1547-
if (_isPeriodProven(dataSetId, endingPeriod, currentPeriod)) {
1548-
provenEpochCount += (toEpoch - settledUpTo);
1549-
settledUpTo = toEpoch;
1535+
if (_isPeriodProven(dataSetId, endingPeriod)) {
1536+
provenEpochCount += (toEpoch - settleUpTo);
1537+
settleUpTo = toEpoch;
15501538
}
15511539
}
1552-
return (provenEpochCount, settledUpTo);
1540+
return (provenEpochCount, settleUpTo);
15531541
}
15541542

1555-
function _isPeriodProven(uint256 dataSetId, uint256 periodId, uint256 currentPeriod) private view returns (bool) {
1556-
if (periodId == currentPeriod) {
1557-
return provenThisPeriod[dataSetId];
1558-
}
1543+
function _isPeriodProven(uint256 dataSetId, uint256 periodId) private view returns (bool) {
15591544
uint256 isProven = provenPeriods[dataSetId][periodId >> 8] & (1 << (periodId & 255));
15601545
return isProven != 0;
15611546
}

service_contracts/test/FilecoinWarmStorageService.t.sol

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5057,17 +5057,54 @@ contract ValidatePaymentTest is FilecoinWarmStorageServiceTest {
50575057
// Validate payment
50585058
FilecoinWarmStorageService.DataSetInfoView memory info = viewContract.getDataSet(dataSetId);
50595059
uint256 fromEpoch = activationEpoch - 1; // exclusive
5060-
uint256 toEpoch = activationEpoch + (maxProvingPeriod * 3) - 1;
5060+
uint256 toEpoch = vm.getBlockNumber() - 1;
50615061
uint256 proposedAmount = 1000e6;
50625062

5063-
vm.prank(address(payments));
50645063
IValidator.ValidationResult memory result =
50655064
pdpServiceWithPayments.validatePayment(info.pdpRailId, proposedAmount, fromEpoch, toEpoch, 0);
50665065

5067-
// Should pay nothing
5066+
// Should settle two unproven periods
5067+
assertEq(result.modifiedAmount, 0, "Should pay nothing");
5068+
assertEq(result.settleUpto, activationEpoch + (maxProvingPeriod * 2), "Should not settle last period");
5069+
assertEq(result.note, "No proven epochs in the requested range");
5070+
5071+
vm.prank(address(mockPDPVerifier));
5072+
pdpServiceWithPayments.nextProvingPeriod(dataSetId, challengeEpoch + maxProvingPeriod * 2, 100, "");
5073+
5074+
// Should settle up to start of current period
5075+
result = pdpServiceWithPayments.validatePayment(info.pdpRailId, proposedAmount, activationEpoch, toEpoch, 0);
5076+
assertEq(result.modifiedAmount, 0, "Should pay nothing");
5077+
assertEq(result.settleUpto, activationEpoch + (maxProvingPeriod * 2), "Should not settle last period");
5078+
assertEq(result.note, "No proven epochs in the requested range");
5079+
5080+
// Never settle less than 1 proving period when that period is unproven
5081+
toEpoch = activationEpoch + 1;
5082+
result = pdpServiceWithPayments.validatePayment(info.pdpRailId, proposedAmount, activationEpoch, toEpoch, 0);
5083+
assertEq(result.modifiedAmount, 0, "Should pay nothing");
5084+
assertEq(result.settleUpto, activationEpoch, "Should not settle");
5085+
assertEq(result.note, "No proven epochs in the requested range");
5086+
5087+
// Never settle less than 1 proving period when that period is unproven
5088+
fromEpoch = activationEpoch + maxProvingPeriod * 2 - 1;
5089+
toEpoch = activationEpoch + maxProvingPeriod * 2 + 1;
5090+
result = pdpServiceWithPayments.validatePayment(info.pdpRailId, proposedAmount, fromEpoch, toEpoch, 0);
50685091
assertEq(result.modifiedAmount, 0, "Should pay nothing");
50695092
assertEq(result.settleUpto, fromEpoch, "Should not settle");
50705093
assertEq(result.note, "No proven epochs in the requested range");
5094+
5095+
// Settle only up to the start of current period
5096+
fromEpoch = activationEpoch + maxProvingPeriod * 2 - 2;
5097+
result = pdpServiceWithPayments.validatePayment(info.pdpRailId, proposedAmount, fromEpoch, toEpoch, 0);
5098+
assertEq(result.modifiedAmount, 0, "Should pay nothing");
5099+
assertEq(result.settleUpto, activationEpoch + maxProvingPeriod * 2, "Should not settle into last period");
5100+
assertEq(result.note, "No proven epochs in the requested range");
5101+
5102+
// Settle only up to the start of current period
5103+
fromEpoch = activationEpoch + maxProvingPeriod / 2;
5104+
result = pdpServiceWithPayments.validatePayment(info.pdpRailId, proposedAmount, fromEpoch, toEpoch, 0);
5105+
assertEq(result.modifiedAmount, 0, "Should pay nothing");
5106+
assertEq(result.settleUpto, activationEpoch + maxProvingPeriod * 2, "Should not settle into last period");
5107+
assertEq(result.note, "No proven epochs in the requested range");
50715108
}
50725109

50735110
/**

0 commit comments

Comments
 (0)