Skip to content

Commit

Permalink
Merge pull request #24 from FILCAT/feat/proofsets
Browse files Browse the repository at this point in the history
Proofset data, Create, Delete
  • Loading branch information
ZenGround0 authored Sep 6, 2024
2 parents 8b6978d + 997d89b commit 867a08f
Show file tree
Hide file tree
Showing 2 changed files with 208 additions and 12 deletions.
151 changes: 147 additions & 4 deletions contracts/src/PDPService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,158 @@ pragma solidity ^0.8.13;

import "@openzeppelin/contracts/access/Ownable.sol";

contract PDPService is Ownable {
contract PDPService {
// Types
// TODO PERF: https://github.com/FILCAT/pdp/issues/16#issuecomment-2329836995
struct Cid {
bytes data;
}

// State fields
uint256 public challengeFinality;

/*
A proof set is the metadata required for tracking data for proof of possession.
It maintains a list of CIDs of data to be proven and metadata needed to
add and remove data to the set and prove possession efficiently.
** logical structure of the proof set**
/*
struct ProofSet {
Cid[] roots;
uint256[] sizes;
uint256[] sumTree;
uint256 size;
address owner;
nextRootID uint64;
}
** PDP service contract tracks many possible proof sets **
[]ProofSet proofsets
To implement this logical structure in the solidity data model we have
two arrays tracking the singleton fields and three two dimensional arrays
tracking the growing data of the proof set. The first index is the proof set id
and the second index is the index of the data in the array.
Invariant: rootCids.length == rootSizes.length == sumTreeSizes.length
*/

// Network epoch delay between last proof of possession and next
// randomness sampling for challenge generation
uint256 challengeFinality;

// TODO PERF: https://github.com/FILCAT/pdp/issues/16#issuecomment-2329838769
uint64 nextProofSetId;
mapping(uint256 => mapping(uint256 => Cid)) rootCids;
mapping(uint256 => mapping(uint256 => uint256)) rootSizes;
mapping(uint256 => mapping(uint256 => uint256)) sumTreeSizes;
mapping(uint256 => uint256) nextRootId;
mapping(uint256 => uint256) proofSetSize;
// ownership of proof set is initialized upon creation to create message sender
// proofset owner has exclusive permission to add and remove roots and delete the proof set
mapping(uint256 => address) proofSetOwner;

// Methods
constructor(uint256 _challengeFinality) Ownable(msg.sender) {
constructor(uint256 _challengeFinality) {
challengeFinality = _challengeFinality;
}

}
// Returns the current challenge finality value
function getChallengeFinality() public view returns (uint256) {
return challengeFinality;
}

// Returns the next proof set ID
function getNextProofSetId() public view returns (uint64) {
return nextProofSetId;
}

// Returns false if the proof set is 1) not yet created 2) deleted
function proofSetLive(uint256 setId) public view returns (bool) {
return setId < nextProofSetId && proofSetOwner[setId] != address(0);
}

// Returns false if the proof set is not live or if the root id is 1) not yet created 2) deleted
function rootLive(uint256 setId, uint256 rootId) public view returns (bool) {
return proofSetLive(setId) && rootId < nextRootId[setId] && rootSizes[setId][rootId] > 0;
}

// Returns the size of a proof set
function getProofSetSize(uint256 setId) public view returns (uint256) {
require(proofSetLive(setId), "Proof set not live");
return proofSetSize[setId];
}

// Returns the next root ID for a proof set
function getNextRootId(uint256 setId) public view returns (uint256) {
require(proofSetLive(setId), "Proof set not live");
return nextRootId[setId];
}

// Returns the owner of a proof set
function getProofSetOwner(uint256 setId) public view returns (address) {
require(proofSetLive(setId), "Proof set not live");
return proofSetOwner[setId];
}

// Returns the root CID for a given proof set and root ID
function getRootCid(uint256 setId, uint256 rootId) public view returns (Cid memory) {
require(proofSetLive(setId), "Proof set not live");
return rootCids[setId][rootId];
}

// Returns the root size for a given proof set and root ID
function getRootSize(uint256 setId, uint256 rootId) public view returns (uint256) {
require(proofSetLive(setId), "Proof set not live");
return rootSizes[setId][rootId];
}

// Returns the sum tree size for a given proof set and root ID
function getSumTreeSize(uint256 setId, uint256 rootId) public view returns (uint256) {
require(proofSetLive(setId), "Proof set not live");
return sumTreeSizes[setId][rootId];
}

// A proof set is created empty, with no roots. Creation yields a proof set ID
// for referring to the proof set later.
// Sender of create message is proof set owner.
function createProofSet() public returns (uint256) {
uint256 setId = nextProofSetId++;
proofSetSize[setId] = 0;
proofSetOwner[setId] = msg.sender;
return setId;
}

// Removes a proof set. Must be called by the contract owner.
function deleteProofSet(uint256 setId) public {
if (setId >= nextProofSetId) {
revert("proof set id out of bounds");
}
require(proofSetOwner[setId] == msg.sender, "Only the owner can delete proof sets");

proofSetSize[setId] = 0;
proofSetOwner[setId] = address(0);
}

// Appends a new root to the collection managed by a proof set.
// Must be called by the contract owner.
function addRoot(uint256 setId, Cid calldata root, uint256 size) public returns (uint256) {
require(proofSetOwner[setId] == msg.sender, "Only the owner can add roots");
// TODO: implement me
return 0;
}

// Removes a root from a proof set. Must be called by the contract owner.
function removeRoot(uint256 setId, uint256 rootId) public {
require(proofSetOwner[setId] == msg.sender, "Only the owner can remove roots");
// TODO: implement me
}

// Verifies and records that the provider proved possession of the
// proof set Merkle roots at some epoch. The challenge seed is determined
// by the previous proof of possession
// TODO: proof will probably be a new type not just bytes
function provePossession(uint256 setId, bytes calldata proof) public {
// TODO: implement me
// TODO: ownership check for proof validation? I don't think its necessary but maybe useful?
}
}
69 changes: 61 additions & 8 deletions contracts/test/PDPService.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,69 @@ pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {PDPService} from "../src/PDPService.sol";

contract PDPServiceOwnershipTest is Test {
PDPService pdpService;

contract PDPServiceProofSetCreateDeleteTest is Test {
PDPService pdpService;

function setUp() public {
pdpService = new PDPService(2);
}

function testOwnerIsConstructorSender() public view {
address expectedOwner = address(this);
address actualOwner = pdpService.owner();
assertEq(expectedOwner, actualOwner, "Owner should be the constructor sender");
}
}
function testCreateProofSet() public {
uint256 setId = pdpService.createProofSet();
assertEq(setId, 0, "First proof set ID should be 0");
assertEq(pdpService.getProofSetSize(setId), 0, "Proof set size should be 0");
assertEq(pdpService.getProofSetOwner(setId), address(this), "Proof set owner should be the constructor sender");
}

function testDeleteProofSet() public {
uint256 setId = pdpService.createProofSet();
pdpService.deleteProofSet(setId);
vm.expectRevert("Proof set not live");
pdpService.getProofSetSize(setId);
}

function testOnlyOwnerCanDeleteProofSet() public {
uint256 setId = pdpService.createProofSet();
// Create a new address to act as a non-owner
address nonOwner = address(0x1234);
// Expect revert when non-owner tries to delete the proof set
vm.prank(nonOwner);
vm.expectRevert("Only the owner can delete proof sets");
pdpService.deleteProofSet(setId);

// Now verify the owner can delete the proof set
pdpService.deleteProofSet(setId);
vm.expectRevert("Proof set not live");
pdpService.getProofSetOwner(setId);
}

// TODO: once we have addRoot we should test deletion of a non empty proof set
function testCannotDeleteNonExistentProofSet() public {
vm.expectRevert("proof set id out of bounds");
pdpService.deleteProofSet(0);
}

function testMethodsOnDeletedProofSetFails() public {
uint256 setId = pdpService.createProofSet();
pdpService.deleteProofSet(setId);
vm.expectRevert("Only the owner can delete proof sets");
pdpService.deleteProofSet(setId);
vm.expectRevert("Proof set not live");
pdpService.getProofSetOwner(setId);
vm.expectRevert("Proof set not live");
pdpService.getProofSetSize(setId);
vm.expectRevert("Proof set not live");
pdpService.getRootCid(setId, 0);
vm.expectRevert("Proof set not live");
pdpService.getRootSize(setId, 0);
vm.expectRevert("Proof set not live");
pdpService.getSumTreeSize(setId, 0);
}

function testGetProofSetID() public {
pdpService.createProofSet();
pdpService.createProofSet();
assertEq(2, pdpService.getNextProofSetId(), "Next proof set ID should be 2");
}
}

0 comments on commit 867a08f

Please sign in to comment.