Skip to content

Commit

Permalink
Merge pull request #86 from FilOzone/feat/first-add-ur-not-special
Browse files Browse the repository at this point in the history
Remove special casing of first add
  • Loading branch information
ZenGround0 authored Dec 13, 2024
2 parents 6a098c7 + f52ced6 commit 9e7eee9
Show file tree
Hide file tree
Showing 5 changed files with 310 additions and 60 deletions.
5 changes: 5 additions & 0 deletions src/IPDPProvingSchedule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ interface IPDPProvingSchedule {
/// @return Challenge window size in epochs
function challengeWindow() external pure returns (uint256);

/// @notice Value for initializing the challenge window start for a given proof set assuming proving period starts now
// @return Initial challenge window start in epochs
function initChallengeWindowStart(uint256 setId) external pure returns (uint256);


/// @notice Calculates the start of the next challenge window for a given proof set
/// @param setId The ID of the proof set
/// @return The block number when the next challenge window starts
Expand Down
32 changes: 18 additions & 14 deletions src/PDPVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable {

// FIL/USD price feed query ID on the Pyth network
bytes32 public constant FIL_USD_PRICE_FEED_ID = 0x150ac9b959aee0051e4091f0ef5216d941f590e1c5e7f91cf7635b5c11628c0e;
uint256 public constant NO_CHALLENGE_SCHEDULED = 0;

// Events
event ProofSetCreated(uint256 indexed setId);
event ProofSetDeleted(uint256 indexed setId, uint256 deletedLeafCount);
event RootsAdded(uint256 indexed firstAdded);
event RootsRemoved(uint256[] indexed rootIds);
event ProofFeePaid(uint256 indexed setId, uint256 fee, uint64 price, int32 expo);
event ProofSetEmpty(uint256 indexed setId);

// Types
// State fields
Expand Down Expand Up @@ -266,7 +268,7 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable {

uint256 setId = nextProofSetId++;
proofSetLeafCount[setId] = 0;
nextChallengeEpoch[setId] = 0; // Re-initialized when the first root is added.
nextChallengeEpoch[setId] = NO_CHALLENGE_SCHEDULED; // Initialized on first call to NextProvingPeriod
proofSetOwner[setId] = msg.sender;
proofSetListener[setId] = listenerAddr;
proofSetLastProvenEpoch[setId] = NO_PROVEN_EPOCH;
Expand Down Expand Up @@ -306,24 +308,18 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable {
}

// Appends new roots to the collection managed by a proof set.
// These roots won't be challenged until the next proving period.
// These roots won't be challenged until the next proving period is
// started by calling nextProvingPeriod.
function addRoots(uint256 setId, RootData[] calldata rootData, bytes calldata extraData) public returns (uint256) {
require(extraData.length <= EXTRA_DATA_MAX_SIZE, "Extra data too large");
require(proofSetLive(setId), "Proof set not live");
require(rootData.length > 0, "Must add at least one root");
require(proofSetOwner[setId] == msg.sender, "Only the owner can add roots");
bool needsChallengeEpoch = nextChallengeEpoch[setId] == 0;
uint256 firstAdded = nextRootId[setId];

for (uint256 i = 0; i < rootData.length; i++) {
addOneRoot(setId, i, rootData[i].root, rootData[i].rawSize);
}
// Initialise the first challenge epoch and challengeable leaf range when the first data is added.
if (needsChallengeEpoch) {
nextChallengeEpoch[setId] = block.number + challengeFinality;
challengeRange[setId] = proofSetLeafCount[setId];
proofSetLastProvenEpoch[setId] = block.number;
}

address listenerAddr = proofSetListener[setId];
if (listenerAddr != address(0)) {
Expand Down Expand Up @@ -390,7 +386,8 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 challengeEpoch = nextChallengeEpoch[setId];
require(block.number >= challengeEpoch, "premature proof");
require(proofs.length > 0, "empty proof");

require(challengeEpoch != NO_CHALLENGE_SCHEDULED, "no challenge scheduled");

uint256 seed = drawChallengeSeed(setId);
uint256 leafCount = challengeRange[setId];
uint256 sumTreeTop = 256 - BitOps.clz(nextRootId[setId]);
Expand Down Expand Up @@ -493,6 +490,12 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable {
function nextProvingPeriod(uint256 setId, uint256 challengeEpoch, bytes calldata extraData) public {
require(extraData.length <= EXTRA_DATA_MAX_SIZE, "Extra data too large");
require(msg.sender == proofSetOwner[setId], "only the owner can move to next proving period");
require(proofSetLeafCount[setId] > 0, "can only start proving once leaves are added");

if (proofSetLastProvenEpoch[setId] == NO_PROVEN_EPOCH) {
proofSetLastProvenEpoch[setId] = block.number;
}

// Take removed roots out of proving set
uint256[] storage removals = scheduledRemovals[setId];
uint256[] memory removalsToProcess = new uint256[](removals.length);
Expand All @@ -505,21 +508,22 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable {
removeRoots(setId, removalsToProcess);
// Bring added roots into proving set
challengeRange[setId] = proofSetLeafCount[setId];
if (challengeEpoch - block.number < challengeFinality) {
if (challengeEpoch < block.number + challengeFinality) {
revert("challenge epoch must be at least challengeFinality epochs in the future");
}
nextChallengeEpoch[setId] = challengeEpoch;

// Clear next challenge epoch if the set is now empty.
// It will be re-set when new data is added.
// It will be re-set after new data is added and nextProvingPeriod is called.
if (proofSetLeafCount[setId] == 0) {
emit ProofSetEmpty(setId);
proofSetLastProvenEpoch[setId] = NO_PROVEN_EPOCH;
nextChallengeEpoch[setId] = 0;
nextChallengeEpoch[setId] = NO_CHALLENGE_SCHEDULED;
}

address listenerAddr = proofSetListener[setId];
if (listenerAddr != address(0)) {
PDPListener(listenerAddr).nextProvingPeriod(setId, challengeEpoch,proofSetLeafCount[setId], extraData);
PDPListener(listenerAddr).nextProvingPeriod(setId, nextChallengeEpoch[setId], proofSetLeafCount[setId], extraData);
}
}

Expand Down
62 changes: 44 additions & 18 deletions src/SimplePDPService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ contract PDPRecordKeeper {
contract SimplePDPService is PDPListener, PDPRecordKeeper, Initializable, UUPSUpgradeable, OwnableUpgradeable {
event FaultRecord(uint256 periodsFaulted);

uint256 public constant NO_CHALLENGE_SCHEDULED = 0;
uint256 public constant NO_PROVING_DEADLINE = 0;

// The address of the PDP verifier contract that is allowed to call this contract
address public pdpVerifierAddress;
mapping(uint256 => uint256) public provingDeadlines;
Expand Down Expand Up @@ -111,10 +114,16 @@ contract SimplePDPService is PDPListener, PDPRecordKeeper, Initializable, UUPSUp
return 60;
}

// Initial value for challenge window start
// Can be used for first call to nextProvingPeriod
function initChallengeWindowStart() public view returns (uint256) {
return block.number + getMaxProvingPeriod() - challengeWindow();
}

// The start of the challenge window for the current proving period
function thisChallengeWindowStart(uint256 setId) public view returns (uint256) {
if (provingDeadlines[setId] == 0) {
revert("Proving not yet started");
if (provingDeadlines[setId] == NO_PROVING_DEADLINE) {
revert("Proving period not yet initialized");
}

uint256 periodsSkipped;
Expand All @@ -128,10 +137,10 @@ contract SimplePDPService is PDPListener, PDPRecordKeeper, Initializable, UUPSUp
}

// The start of the NEXT OPEN proving period's challenge window
// Useful for querying before nextProvingPeriod to determine challengeEpoch to submit for nextProvingPeriod
// Useful for querying before nextProvingPeriod to determine challengeEpoch to submit for nextProvingPeriod
function nextChallengeWindowStart(uint256 setId) public view returns (uint256) {
if (provingDeadlines[setId] == 0) {
revert("Proving not yet started");
if (provingDeadlines[setId] == NO_PROVING_DEADLINE) {
revert("Proving period not yet initialized");
}
// If the current period is open this is the next period's challenge window
if (block.number <= provingDeadlines[setId]) {
Expand All @@ -157,9 +166,6 @@ contract SimplePDPService is PDPListener, PDPRecordKeeper, Initializable, UUPSUp
}

function rootsAdded(uint256 proofSetId, uint256 firstAdded, PDPVerifier.RootData[] memory rootData, bytes calldata) external onlyPDPVerifier {
if (firstAdded == 0) {
provingDeadlines[proofSetId] = block.number + getMaxProvingPeriod();
}
receiveProofSetEvent(proofSetId, OperationType.ADD, abi.encode(firstAdded, rootData));
}

Expand All @@ -177,38 +183,58 @@ contract SimplePDPService is PDPListener, PDPRecordKeeper, Initializable, UUPSUp
if (challengeCount < getChallengesPerProof()) {
revert("Invalid challenge count < 5");
}
// check for proof outside of proving period
if (provingDeadlines[proofSetId] == NO_PROVING_DEADLINE) {
revert("Proving not yet started");
}
// check for proof outside of challenge window
if (provingDeadlines[proofSetId] < block.number) {
revert("Current proving period passed. Open a new proving period.");
}
if (provingDeadlines[proofSetId] - getMaxProvingPeriod() >= block.number) {
revert("Too early. Wait for proving period to open");
if (provingDeadlines[proofSetId] - challengeWindow() > block.number) {
revert("Too early. Wait for challenge window to open");
}
provenThisPeriod[proofSetId] = true;
}

// nextProvingPeriod checks for unsubmitted proof and emits a fault if so
function nextProvingPeriod(uint256 proofSetId, uint256 challengeEpoch, uint256 leafCount, bytes calldata) external onlyPDPVerifier {
receiveProofSetEvent(proofSetId, OperationType.NEXT_PROVING_PERIOD, abi.encode(challengeEpoch, leafCount));
// Noop when proving period not yet open

// initialize state for new proofset
if (provingDeadlines[proofSetId] == NO_PROVING_DEADLINE) {
uint256 firstDeadline = block.number + getMaxProvingPeriod();
if (challengeEpoch < firstDeadline - challengeWindow() || challengeEpoch > firstDeadline) {
revert("Next challenge epoch must fall within the next challenge window");
}
provingDeadlines[proofSetId] = firstDeadline;
provenThisPeriod[proofSetId] = false;
return;
}

// Revert when proving period not yet open
// Can only get here if calling nextProvingPeriod multiple times within the same proving period
uint256 prevDeadline = provingDeadlines[proofSetId] - getMaxProvingPeriod();
if (block.number <= prevDeadline) {
revert("One call to nextProvingPeriod allowed per proving period");
}

uint256 periodsSkipped;
// Proving period is open 0 skipped periods
if (block.number <= provingDeadlines[proofSetId]) {
periodsSkipped = 0;
} else { // Proving period has closed possibly some skipped periods
periodsSkipped = (block.number - (provingDeadlines[proofSetId] + 1)) / getMaxProvingPeriod();
}
// ensure next challenge epoch falls within the next challenge window.
// The next challenge window immediately precedes the next deadline
uint256 nextDeadline = provingDeadlines[proofSetId] + getMaxProvingPeriod()*(periodsSkipped+1);
if (challengeEpoch < nextDeadline - challengeWindow() || challengeEpoch > nextDeadline) {
revert("Next challenge epoch must fall within the next challenge window");

uint256 nextDeadline;
// the proofset has become empty and provingDeadline is set inactive
if (challengeEpoch == NO_CHALLENGE_SCHEDULED) {
nextDeadline = NO_PROVING_DEADLINE;
} else {
nextDeadline = provingDeadlines[proofSetId] + getMaxProvingPeriod()*(periodsSkipped+1);
if (challengeEpoch < nextDeadline - challengeWindow() || challengeEpoch > nextDeadline) {
revert("Next challenge epoch must fall within the next challenge window");
}
}
uint256 faultPeriods = periodsSkipped;
if (!provenThisPeriod[proofSetId]) {
Expand Down
Loading

0 comments on commit 9e7eee9

Please sign in to comment.