@@ -12,8 +12,9 @@ import {IInbox} from "src/protocol/IInbox.sol";
1212/// Represents states where the previous period has existing publications (that can be proven).
1313/// This performs the same tests as CurrentPeriodHasPublications except it applies to proofs for the previous period.
1414/// We could merge them and check every period but that is probably excessive.
15+ /// This also includes tests to finalize the previous period. For simplicity (since no other tests are affected),
16+ /// we treat the "pastDeadline" options as different tests rather than different states.
1517abstract contract PreviousPeriodHasPublications is CurrentPeriodHasPublications {
16-
1718 // This is a sanity check to ensure we're in the expected state
1819 function test_PreviousPeriodHasPublications_confirmPreconditions () public view {
1920 uint256 periodId = proverManager.currentPeriodId ();
@@ -53,7 +54,7 @@ abstract contract PreviousPeriodHasPublications is CurrentPeriodHasPublications
5354 }
5455
5556 function test_PreviousPeriodHasPublications_prove_shouldRevertIfFirstPublicationInPreviousPeriod () public {
56- uint256 periodId = proverManager.currentPeriodId () - 1 ;
57+ uint256 periodId = proverManager.currentPeriodId () - 1 ;
5758 _proveWholePeriod (periodId);
5859 firstPub.timestamp -= 1 ;
5960 vm.expectRevert (BaseProverManager.FirstPublicationIsBeforePeriod.selector );
@@ -81,28 +82,137 @@ abstract contract PreviousPeriodHasPublications is CurrentPeriodHasPublications
8182 function test_PreviousPeriodHasPublications_prove_shouldCreditProver () public {
8283 uint256 periodId = proverManager.currentPeriodId () - 1 ;
8384 _proveWholePeriod (periodId);
84- LibProvingPeriod.Period memory period = proverManager.getPeriod (periodId);
85+ LibProvingPeriod.Period memory periodBefore = proverManager.getPeriod (periodId);
8586
8687 uint256 standardFee =
87- period .fee * (checkpointTracker.nPublications () - checkpointTracker.nDelayedPublications ());
88- uint256 delayedFee = LibPercentage.scaleByPercentage (period .fee, period .delayedFeePercentage)
88+ periodBefore .fee * (checkpointTracker.nPublications () - checkpointTracker.nDelayedPublications ());
89+ uint256 delayedFee = LibPercentage.scaleByPercentage (periodBefore .fee, periodBefore .delayedFeePercentage)
8990 * checkpointTracker.nDelayedPublications ();
9091 uint256 reward = standardFee + delayedFee;
9192
92- bool isDeadlinePassed = period .deadline != 0 && vm.getBlockTimestamp () > period .deadline;
93- address prover = isDeadlinePassed ? proverB : period .prover;
93+ bool isDeadlinePassed = periodBefore .deadline != 0 && vm.getBlockTimestamp () > periodBefore .deadline;
94+ address newProver = isDeadlinePassed ? proverB : periodBefore .prover;
9495
9596 uint256 escrowedBefore = _currencyBalance (address (proverManager));
96- uint256 balanceBefore = proverManager.balances (prover );
97+ uint256 balanceBefore = proverManager.balances (newProver );
9798
9899 vm.prank (proverB);
99100 proverManager.prove (start, end, firstPub, lastPub, proof, periodId);
100101
101102 uint256 escrowedAfter = _currencyBalance (address (proverManager));
102- uint256 balanceAfter = proverManager.balances (prover);
103+ uint256 balanceAfter = proverManager.balances (newProver);
104+ LibProvingPeriod.Period memory periodAfter = proverManager.getPeriod (periodId);
103105
104106 assertEq (escrowedAfter, escrowedBefore, "Value held by ProverManager changed " );
105107 assertEq (balanceAfter, balanceBefore + reward, "Balance not updated correctly " );
106- assertEq (proverManager.getPeriod (periodId).pastDeadline, isDeadlinePassed, "PastDeadline flag set incorrectly " );
108+ assertEq (periodAfter.pastDeadline, isDeadlinePassed, "PastDeadline flag set incorrectly " );
109+ assertEq (periodAfter.prover, newProver, "New prover set incorrectly " );
110+ }
111+
112+ function test_PreviousPeriodHasPublications_finalizePastPeriod_shouldRevertWithUnknownPublication () public {
113+ uint256 periodId = proverManager.currentPeriodId () - 1 ;
114+ IInbox.PublicationHeader memory provenPublication;
115+ inbox.setInvalidHeader (provenPublication);
116+ vm.expectRevert (BaseProverManager.InvalidPublication.selector );
117+ proverManager.finalizePastPeriod (periodId, provenPublication);
118+ }
119+
120+ function test_PreviousPeriodHasPublications_finalizePastPeriod_shouldRevertWithUnprovenPublication () public {
121+ uint256 periodId = proverManager.currentPeriodId () - 1 ;
122+ IInbox.PublicationHeader memory provenPublication; // despite the name this will be unproven.
123+ provenPublication.id = checkpointTracker.provenPublicationId () + 1 ;
124+ vm.expectRevert (BaseProverManager.PublicationNotProven.selector );
125+ proverManager.finalizePastPeriod (periodId, provenPublication);
126+ }
127+
128+ function test_PreviousPeriodHasPublications_finalizePastPeriod_cannotFinalizeVacantPeriod () public {
129+ uint256 periodId = proverManager.currentPeriodId () - 1 ;
130+ IInbox.PublicationHeader memory provenPublication;
131+ provenPublication.id = checkpointTracker.provenPublicationId ();
132+ address prover = proverManager.getPeriod (periodId).prover;
133+ vm.expectRevert (BaseProverManager.PeriodNotInitialized.selector , prover == address (0 ) ? 1 : 0 );
134+ proverManager.finalizePastPeriod (periodId, provenPublication);
135+ }
136+
137+ function test_PreviousPeriodHasPublications_finalizePastPeriod_shouldRevertWithEarlyPublication () public {
138+ uint256 periodId = proverManager.currentPeriodId () - 1 ;
139+ LibProvingPeriod.Period memory period = proverManager.getPeriod (periodId);
140+ if (period.prover == address (0 )) return ; // skip vacant periods
141+
142+ IInbox.PublicationHeader memory provenPublication;
143+ provenPublication.id = checkpointTracker.provenPublicationId ();
144+ provenPublication.timestamp = period.end;
145+ vm.expectRevert (BaseProverManager.PublicationNotAfterPeriod.selector );
146+ proverManager.finalizePastPeriod (periodId, provenPublication);
147+ }
148+
149+ function test_PreviousPeriodHasPublications_finalizePastPeriod_shouldFinalizePeriod () public {
150+ uint256 periodId = proverManager.currentPeriodId () - 1 ;
151+ LibProvingPeriod.Period memory periodBefore = proverManager.getPeriod (periodId);
152+ if (periodBefore.prover == address (0 )) return ; // skip vacant periods
153+
154+ IInbox.PublicationHeader memory provenPublication;
155+ provenPublication.id = checkpointTracker.provenPublicationId ();
156+ provenPublication.timestamp = periodBefore.end + 1 ;
157+ proverManager.finalizePastPeriod (periodId, provenPublication);
158+
159+ LibProvingPeriod.Period memory periodAfter = proverManager.getPeriod (periodId);
160+ assertEq (periodAfter.prover, address (0 ), "Prover not cleared " );
161+ assertEq (periodAfter.stake, 0 , "Stake not cleared " );
162+ }
163+
164+ function test_PreviousPeriodHasPublications_finalizePastPeriod_shouldReturnFullStakeToTimelyProver () public {
165+ uint256 periodId = proverManager.currentPeriodId () - 1 ;
166+ LibProvingPeriod.Period memory period = proverManager.getPeriod (periodId);
167+ if (period.prover == address (0 )) return ; // skip vacant periods
168+
169+ // This is the default situation, so this is just a pre-condition check.
170+ assertFalse (period.pastDeadline, "Period had missed deadline " );
171+
172+ IInbox.PublicationHeader memory provenPublication;
173+ provenPublication.id = checkpointTracker.provenPublicationId ();
174+ provenPublication.timestamp = period.end + 1 ;
175+
176+ uint256 escrowedBefore = _currencyBalance (address (proverManager));
177+ uint256 balanceBefore = proverManager.balances (period.prover);
178+
179+ proverManager.finalizePastPeriod (periodId, provenPublication);
180+
181+ uint256 escrowedAfter = _currencyBalance (address (proverManager));
182+ uint256 balanceAfter = proverManager.balances (period.prover);
183+
184+ assertEq (escrowedAfter, escrowedBefore, "Value held by ProverManager changed " );
185+ assertEq (balanceAfter, balanceBefore + period.stake, "Balance not updated correctly " );
186+ }
187+
188+ function test_PreviousPeriodHasPublications_finalizePastPeriod_shouldTransferReducedStakeToNewestProver () public {
189+ uint256 periodId = proverManager.currentPeriodId () - 1 ;
190+ LibProvingPeriod.Period memory period = proverManager.getPeriod (periodId);
191+ if (period.prover == address (0 )) return ; // skip vacant periods
192+ if (period.deadline == 0 || vm.getBlockTimestamp () <= period.deadline) return ; // skip periods within deadline
193+
194+ // Prove the period now (after the deadline) to set the `pastDeadline` flag
195+ _proveWholePeriod (periodId);
196+ vm.prank (proverB);
197+ proverManager.prove (start, end, firstPub, lastPub, proof, periodId);
198+ LibProvingPeriod.Period memory periodAfterProof = proverManager.getPeriod (periodId);
199+ assertTrue (periodAfterProof.pastDeadline, "Period proven within deadline " );
200+ assertEq (periodAfterProof.prover, proverB, "Period not assigned to new prover " );
201+
202+ IInbox.PublicationHeader memory provenPublication;
203+ provenPublication.id = checkpointTracker.provenPublicationId ();
204+ provenPublication.timestamp = period.end + 1 ;
205+
206+ uint256 escrowedBefore = _currencyBalance (address (proverManager));
207+ uint256 balanceBefore = proverManager.balances (proverB);
208+
209+ proverManager.finalizePastPeriod (periodId, provenPublication);
210+
211+ uint256 escrowedAfter = _currencyBalance (address (proverManager));
212+ uint256 balanceAfter = proverManager.balances (proverB);
213+ uint256 rewardAmount = LibPercentage.scaleByBPS (period.stake, proverManager.rewardFraction ());
214+
215+ assertEq (escrowedAfter, escrowedBefore, "Value held by ProverManager changed " );
216+ assertEq (balanceAfter, balanceBefore + rewardAmount, "Balance not updated correctly " );
107217 }
108218}
0 commit comments