diff --git a/src/Fees.sol b/src/Fees.sol index 8b80600a..16940b21 100644 --- a/src/Fees.sol +++ b/src/Fees.sol @@ -4,11 +4,8 @@ pragma solidity ^0.8.20; /// @title PDPFees /// @notice A library for calculating fees for the PDP. library PDPFees { - uint256 constant ATTO_FIL = 1; - uint256 constant FIL_TO_ATTO_FIL = 1e18 * ATTO_FIL; - - // 0.1 FIL - uint256 constant SYBIL_FEE = FIL_TO_ATTO_FIL / 10; + // 0.1 FIL cleanup bond held per data set, returned to whoever finalizes cleanup + uint256 constant CLEANUP_DEPOSIT = 0.1 ether; // Default FIL-based proof fee: 0.00023 FIL per TiB (used for initialization) // Based on: 0.00067 USD per TiB / 2.88 USD per FIL = 0.00023 FIL per TiB @@ -29,9 +26,8 @@ library PDPFees { return (feePerTiB * rawSize) >> 40; } - // sybil fee adds cost to adding state to the pdp verifier contract to prevent - // wasteful state growth. 0.1 FIL - function sybilFee() internal pure returns (uint256) { - return SYBIL_FEE; + // cleanup deposit is held per data set and paid out to whoever completes cleanup. 0.1 FIL + function cleanupDeposit() internal pure returns (uint256) { + return CLEANUP_DEPOSIT; } } diff --git a/src/PDPVerifier.sol b/src/PDPVerifier.sol index db625c30..b73b746b 100644 --- a/src/PDPVerifier.sol +++ b/src/PDPVerifier.sol @@ -13,13 +13,6 @@ import {FVMPay} from "fvm-solidity/FVMPay.sol"; import {FVMRandom} from "fvm-solidity/FVMRandom.sol"; import {IPDPTypes} from "./interfaces/IPDPTypes.sol"; -interface IFilecoinPay { - function accounts(address token, address owner) - external - view - returns (uint256 funds, uint256 lockupCurrent, uint256 lockupRate, uint256 lockupLastSettledAt); -} - /// @title PDPListener /// @notice Interface for PDP Service applications managing data storage. /// @dev This interface exists to provide an extensible hook for applications to use the PDP verification contract @@ -45,13 +38,18 @@ interface PDPListener { } uint256 constant NEW_DATA_SET_SENTINEL = 0; +// Defaults +uint256 constant NO_CHALLENGE_SCHEDULED = 0; +uint256 constant NO_PROVEN_EPOCH = 0; contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { // Constants uint256 public constant MAX_PIECE_SIZE_LOG2 = 50; uint256 public constant MAX_ENQUEUED_REMOVALS = 2000; - uint256 public constant NO_CHALLENGE_SCHEDULED = 0; - uint256 public constant NO_PROVEN_EPOCH = 0; + + // Cleanup + uint256 private constant CLEANUP_MODE_SENTINEL = type(uint256).max; + uint256 public constant INACTIVITY_WINDOW = 86400; // Upgrade sequence number, used by Initializable.reinitializer uint64 private immutable REINITIALIZER_VERSION; @@ -109,14 +107,24 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { // randomness sampling for challenge generation. // // The purpose of this delay is to prevent SPs from biasing randomness by running forking attacks. - // Given a small enough challengeFinality an SP can run several trials of challenge sampling and + // Given a small enough CHALLENGE_FINALITY an SP can run several trials of challenge sampling and // fork around samples that don't suit them, grinding the challenge randomness. // For the filecoin L1, a safe value is 150 using the same analysis setting 150 epochs between // PoRep precommit and PoRep provecommit phases. // // We keep this around for future portability to a variety of environments with different assumptions // behind their challenge randomness sampling methods. - uint256 challengeFinality; + + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + uint256 private immutable CHALLENGE_FINALITY; + uint256 private constant MAX_FINALITY = INACTIVITY_WINDOW / 2; + // Block number when this implementation was deployed. Used as the activity baseline for legacy + // datasets (dataSetLastProvenEpoch == 0) so they cannot be permissionlessly deleted immediately + // after upgrade. + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + uint256 public immutable LEGACY_ACTIVITY_EPOCH; + + uint256 deprecatedChallengeFinality; // TODO PERF: https://github.com/FILCAT/pdp/issues/16#issuecomment-2329838769 uint64 nextDataSetId; @@ -156,14 +164,6 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { FeeStatus private feeStatus; - // USDFC sybil fee support - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - address public immutable USDFC_TOKEN_ADDRESS; - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - uint256 public immutable USDFC_SYBIL_FEE; - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - address public immutable PAYMENTS_CONTRACT_ADDRESS; - // Used for announcing upgrades, packed into one slot struct PlannedUpgrade { // Address of the new implementation contract @@ -174,32 +174,30 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { PlannedUpgrade public nextUpgrade; + // FIL deposit collected at createDataSet and returned to whoever finalizes cleanup for that data set. + mapping(uint256 => uint256) cleanupDeposit; + // Block number when deleteDataSet was called, used to gate permissionless cleanupPieces calls. + mapping(uint256 => uint256) cleanupModeEpoch; + // Methods /// @custom:oz-upgrades-unsafe-allow constructor - constructor( - uint64 _initializerVersion, - address _usdfcTokenAddress, - uint256 _usdfcSybilFee, - address _paymentsContractAddress - ) { + constructor(uint64 _initializerVersion, uint256 _challengeFinality) { + require(_challengeFinality < MAX_FINALITY / 10, ExcessiveChallengeDelay(_challengeFinality, MAX_FINALITY / 10)); _disableInitializers(); - require(_usdfcSybilFee > 0, "USDFC sybil fee must be greater than 0"); REINITIALIZER_VERSION = _initializerVersion; - USDFC_TOKEN_ADDRESS = _usdfcTokenAddress; - USDFC_SYBIL_FEE = _usdfcSybilFee; - PAYMENTS_CONTRACT_ADDRESS = _paymentsContractAddress; + CHALLENGE_FINALITY = _challengeFinality; + LEGACY_ACTIVITY_EPOCH = block.number; } - function initialize(uint256 _challengeFinality) public initializer { + function initialize() public initializer { __Ownable_init(msg.sender); __UUPSUpgradeable_init(); - challengeFinality = _challengeFinality; nextDataSetId = 1; // Data sets start at 1 feeStatus.nextFeePerTiB = PDPFees.DEFAULT_FEE_PER_TIB; } - string public constant VERSION = "3.2.0"; + string public constant VERSION = "3.3.0"; event ContractUpgraded(string version, address implementation); event UpgradeAnnounced(PlannedUpgrade plannedUpgrade); @@ -228,50 +226,21 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { require(success, "Burn failed"); } - // Validates msg.value meets sybil fee requirement and burns the fee. - // Returns the sybil fee amount for later refund calculation. - function _validateAndBurnSybilFee() internal returns (uint256 sybilFee) { - sybilFee = PDPFees.sybilFee(); - require(msg.value >= sybilFee, "sybil fee not met"); - burnFee(sybilFee); - } - - // Refunds any amount sent over the sybil fee back to msg.sender. - // Must be called after all state changes to avoid re-entrancy issues. - function _refundExcessSybilFee(uint256 sybilFee) internal { - if (msg.value > sybilFee) { - (bool success,) = msg.sender.call{value: msg.value - sybilFee}(""); - require(success, "Transfer failed."); - } - } - - function ensureBurned(bool usdfcBurned, bool defaultToFilBurn) internal { - if (!usdfcBurned) { - if (defaultToFilBurn) { - uint256 sybilFee = _validateAndBurnSybilFee(); - _refundExcessSybilFee(sybilFee); - } else { - revert UsdfcSybilFeeNotMet(); - } - } else { - // USDFC burned, refund any FIL sent - if (msg.value > 0) { - (bool success,) = msg.sender.call{value: msg.value}(""); - if (!success) revert FilRefundFailed(); - } + // Handles fee payment for dataset creation: requires cleanup deposit in FIL. Stores the deposit for setId. + // FVMPay.pay uses the SEND method (0) which transfers value without executing recipient code, so it cannot re-enter. + function _handleFeesWithDeposit(uint256 setId) internal { + uint256 deposit = PDPFees.cleanupDeposit(); + require(msg.value >= deposit, CleanupDepositRequired()); + cleanupDeposit[setId] = deposit; + uint256 excess = msg.value - deposit; + if (excess > 0) { + require(FVMPay.pay(msg.sender, excess), TransferFailed()); } } - function _getPaymentsUsdfcBalance() internal view returns (uint256) { - if (PAYMENTS_CONTRACT_ADDRESS == address(0) || USDFC_TOKEN_ADDRESS == address(0)) return 0; - (uint256 funds,,,) = - IFilecoinPay(PAYMENTS_CONTRACT_ADDRESS).accounts(USDFC_TOKEN_ADDRESS, PAYMENTS_CONTRACT_ADDRESS); - return funds; - } - // Returns the current challenge finality value function getChallengeFinality() public view returns (uint256) { - return challengeFinality; + return CHALLENGE_FINALITY; } // Returns the next data set ID @@ -279,9 +248,10 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { return nextDataSetId; } - // Returns false if the data set is 1) not yet created 2) deleted + // Returns false if the data set is 1) not yet created 2) deleted or in cleanup mode function dataSetLive(uint256 setId) public view returns (bool) { - return setId < nextDataSetId && storageProvider[setId] != address(0); + return setId < nextDataSetId && storageProvider[setId] != address(0) + && nextChallengeEpoch[setId] != CLEANUP_MODE_SENTINEL; } // Returns false if the data set is not live or if the piece id is 1) not yet created 2) deleted @@ -581,10 +551,9 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { function _createDataSet(address listenerAddr, bytes memory extraData) internal returns (uint256) { uint256 setId = nextDataSetId++; dataSetLeafCount[setId] = 0; - nextChallengeEpoch[setId] = NO_CHALLENGE_SCHEDULED; // initialized on first call to NextProvingPeriod storageProvider[setId] = msg.sender; dataSetListener[setId] = listenerAddr; - dataSetLastProvenEpoch[setId] = NO_PROVEN_EPOCH; + dataSetLastProvenEpoch[setId] = block.number; if (listenerAddr != address(0)) { PDPListener(listenerAddr).dataSetCreated(setId, msg.sender, extraData); @@ -600,39 +569,129 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { // Parameters: // - listenerAddr: Address of PDPListener contract to receive callbacks (can be address(0) for no listener) // - extraData: Arbitrary bytes passed to listener's dataSetCreated callback - // - defaultToFilBurn: If true, falls back to FIL burn when USDFC not available. If false, reverts. - // - msg.value: Must include sybil fee (PDPFees.sybilFee()) when using FIL fallback, excess is refunded + // - msg.value: Must include cleanup deposit (PDPFees.cleanupDeposit()). Excess is refunded. // // Returns: The newly created data set ID // // Only the storage provider (msg.sender) can call this function. function createDataSet(address listenerAddr, bytes calldata extraData) public payable returns (uint256) { - uint256 balanceBefore = _getPaymentsUsdfcBalance(); uint256 setId = _createDataSet(listenerAddr, extraData); - uint256 balanceAfter = _getPaymentsUsdfcBalance(); - bool defaultToFilBurn = msg.value > 0; - ensureBurned(balanceAfter >= balanceBefore + USDFC_SYBIL_FEE, defaultToFilBurn); + _handleFeesWithDeposit(setId); return setId; } - // Removes a data set. Must be called by the storage provider. + // Removes a data set. Normally called by the storage provider; permissionless after INACTIVITY_WINDOW + // blocks of SP inactivity (measured from dataSetLastProvenEpoch). + // + // After this call the data set is no longer live. If pieces remain, cleanup mode is entered and + // cleanupPieces must be called to finish storage teardown and collect the cleanup deposit. + // For zero-piece data sets, cleanup is finalized immediately and the deposit is paid to msg.sender. function deleteDataSet(uint256 setId, bytes calldata extraData) public { if (setId >= nextDataSetId) { revert("data set id out of bounds"); } - require(storageProvider[setId] == msg.sender, "Only the storage provider can delete data sets"); + address sp = storageProvider[setId]; + require(sp != address(0), DataSetNotLive()); + require(nextChallengeEpoch[setId] != CLEANUP_MODE_SENTINEL, DataSetAlreadyInCleanup()); + + // Permissionless if the SP has been inactive for more than INACTIVITY_WINDOW blocks. + // Legacy datasets (lastProvenEpoch == 0) use the implementation deployment block as baseline. + uint256 lastActivity = dataSetLastProvenEpoch[setId]; + if (lastActivity == NO_PROVEN_EPOCH) { + lastActivity = LEGACY_ACTIVITY_EPOCH; + } + if (block.number <= lastActivity + INACTIVITY_WINDOW) { + require(msg.sender == sp, OnlyStorageProviderCanDelete()); + } + uint256 deletedLeafCount = dataSetLeafCount[setId]; dataSetLeafCount[setId] = 0; - storageProvider[setId] = address(0); - nextChallengeEpoch[setId] = 0; - dataSetLastProvenEpoch[setId] = NO_PROVEN_EPOCH; address listenerAddr = dataSetListener[setId]; if (listenerAddr != address(0)) { PDPListener(listenerAddr).dataSetDeleted(setId, deletedLeafCount, extraData); } emit DataSetDeleted(setId, deletedLeafCount); + + if (nextPieceId[setId] == 0) { + // Zero-piece data set: finalize cleanup immediately and pay deposit to caller. + _finalizeCleanup(setId); + } else { + // Pieces remain: enter cleanup mode. storageProvider and dataSetLastProvenEpoch are kept + // so cleanupPieces can apply the same permission gate. + nextChallengeEpoch[setId] = CLEANUP_MODE_SENTINEL; + cleanupModeEpoch[setId] = block.number; + } + } + + // Releases storage for a deleted data set piece-by-piece. Can only be called after deleteDataSet + // has placed the data set in cleanup mode (nextChallengeEpoch == CLEANUP_MODE_SENTINEL), or for + // legacy data sets deleted before this feature was added (storageProvider == 0 && nextPieceId > 0). + // + // The caller gate mirrors deleteDataSet: SP-exclusive within INACTIVITY_WINDOW blocks of deleteDataSet, + // permissionless after. Legacy data sets are always permissionless. + // + // On the final call that clears all pieces, all remaining data set state is also cleared and + // the cleanup deposit is transferred to msg.sender. Returns true when cleanup is complete. + function cleanupPieces(uint256 setId, uint256 maxPieces) external returns (bool done) { + require(maxPieces > 0, MaxPiecesMustBePositive()); + + bool isCleanupMode = nextChallengeEpoch[setId] == CLEANUP_MODE_SENTINEL; + // Legacy data sets deleted before this feature: storageProvider already zeroed, pieces remain. + bool isLegacyDataset = storageProvider[setId] == address(0) && nextPieceId[setId] > 0; + + require(isCleanupMode || isLegacyDataset, DataSetNotInCleanupMode()); + + if (isCleanupMode) { + // Same inactivity gate as deleteDataSet, anchored to when cleanup mode was entered. + if (block.number <= cleanupModeEpoch[setId] + INACTIVITY_WINDOW) { + require(msg.sender == storageProvider[setId], OnlyStorageProviderCanCleanupPieces()); + } + } + + uint256 pieceCount = nextPieceId[setId]; + uint256 toClean = pieceCount < maxPieces ? pieceCount : maxPieces; + + for (uint256 i = 0; i < toClean; i++) { + uint256 pieceId = pieceCount - 1 - i; + delete pieceCids[setId][pieceId]; + delete pieceLeafCounts[setId][pieceId]; + delete sumTreeCounts[setId][pieceId]; + } + + nextPieceId[setId] = pieceCount - toClean; + + if (nextPieceId[setId] == 0) { + _finalizeCleanup(setId); + done = true; + } + } + + // Clears all remaining singleton state for a data set and transfers the cleanup deposit to msg.sender. + // Must only be called when nextPieceId[setId] == 0. + function _finalizeCleanup(uint256 setId) internal { + // Clear scheduled removal bitmap entries before deleting the array. + uint256[] storage removals = scheduledRemovals[setId]; + for (uint256 i = 0; i < removals.length; i++) { + delete scheduledRemovalsBitmap[setId][removals[i] >> 8]; + } + delete scheduledRemovals[setId]; + + delete dataSetListener[setId]; + delete dataSetProposedStorageProvider[setId]; + delete challengeRange[setId]; + delete storageProvider[setId]; + delete dataSetLastProvenEpoch[setId]; + delete nextChallengeEpoch[setId]; + delete cleanupModeEpoch[setId]; + + uint256 deposit = cleanupDeposit[setId]; + delete cleanupDeposit[setId]; + + if (deposit > 0) { + require(FVMPay.pay(msg.sender, deposit), DepositTransferFailed()); + } } // Appends pieces to a data set. Optionally creates a new data set if setId == 0. @@ -653,7 +712,7 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { // - extraData: abi.encode(bytes createPayload, bytes addPayload) where: // - createPayload: passed to listener's dataSetCreated callback // - addPayload: passed to listener's piecesAdded callback (if pieces added) - // - msg.value: must include sybil fee (PDPFees.sybilFee()), excess is refunded + // - msg.value: must include cleanup deposit (PDPFees.cleanupDeposit()), excess is refunded // - Returns: the newly created data set ID // // Only the storage provider can call this function. @@ -667,17 +726,14 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { require(listenerAddr != address(0), "listener required for new dataset"); - uint256 balanceBefore = _getPaymentsUsdfcBalance(); uint256 newSetId = _createDataSet(listenerAddr, createPayload); - uint256 balanceAfter = _getPaymentsUsdfcBalance(); // Add pieces to the newly created data set (if any) if (pieceData.length > 0) { _addPiecesToDataSet(newSetId, pieceData, addPayload); } - bool defaultToFilBurn = msg.value > 0; - ensureBurned(balanceAfter >= balanceBefore + USDFC_SYBIL_FEE, defaultToFilBurn); + _handleFeesWithDeposit(newSetId); return newSetId; } else { // Adding to an existing set; no fee should be sent and listenerAddr must be zero @@ -718,8 +774,17 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { } error IndexedError(uint256 idx, string msg); - error UsdfcSybilFeeNotMet(); - error FilRefundFailed(); + error CleanupDepositRequired(); + error DataSetNotLive(); + error DataSetAlreadyInCleanup(); + error OnlyStorageProviderCanDelete(); + error MaxPiecesMustBePositive(); + error DataSetNotInCleanupMode(); + error OnlyStorageProviderCanCleanupPieces(); + error DepositTransferFailed(); + error TransferFailed(); + error InsufficientChallengeDelay(uint256 epochs, uint256 minDelay); + error ExcessiveChallengeDelay(uint256 epochs, uint256 maxDelay); function addOnePiece(uint256 setId, uint256 callIdx, Cids.Cid calldata piece) internal returns (uint256) { (uint256 padding, uint8 height,) = Cids.validateCommPv2(piece); @@ -840,8 +905,8 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { emit PossessionProven(setId, challenges); - // Return the overpayment after doing everything else to avoid re-entrancy issues (all state has been updated by this point). If this - // call fails, the entire operation reverts. + // Return the overpayment. FVMPay.pay uses SEND (method 0) which transfers value without executing recipient code and cannot re-enter. + // If the transfer fails, the entire operation reverts. if (refund > 0) { bool success = FVMPay.pay(msg.sender, refund); require(success, "Transfer failed."); @@ -874,8 +939,8 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { return block.timestamp >= feeStatus.transitionTime ? feeStatus.nextFeePerTiB : feeStatus.currentFeePerTiB; } - function FIL_SYBIL_FEE() external pure returns (uint256) { - return PDPFees.sybilFee(); + function FIL_CLEANUP_DEPOSIT() external pure returns (uint256) { + return PDPFees.cleanupDeposit(); } // Public getters for packed fee status @@ -909,7 +974,7 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { // and can be deleted. And pieces added in the last proving period must be available for challenging. // // Additionally this method forces sampling of a new challenge. It enforces that the new - // challenge epoch is at least `challengeFinality` epochs in the future. + // challenge epoch is at least `CHALLENGE_FINALITY` epochs in the future. // // Note that this method can be called at any time but the pdpListener will likely consider it // a "fault" or other penalizeable behavior to call this method before calling provePossesion. @@ -946,8 +1011,10 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { // Bring added pieces into proving set challengeRange[setId] = dataSetLeafCount[setId]; - if (challengeEpoch < block.number + challengeFinality) { - revert("challenge epoch must be at least challengeFinality epochs in the future"); + unchecked { + uint256 delayEpochs = challengeEpoch - block.number; + require(delayEpochs >= CHALLENGE_FINALITY, InsufficientChallengeDelay(delayEpochs, CHALLENGE_FINALITY)); + require(delayEpochs <= MAX_FINALITY, ExcessiveChallengeDelay(delayEpochs, MAX_FINALITY)); } nextChallengeEpoch[setId] = challengeEpoch; @@ -955,7 +1022,7 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable { // It will be re-set after new data is added and nextProvingPeriod is called. if (dataSetLeafCount[setId] == 0) { emit DataSetEmpty(setId); - dataSetLastProvenEpoch[setId] = NO_PROVEN_EPOCH; + dataSetLastProvenEpoch[setId] = block.number; nextChallengeEpoch[setId] = NO_CHALLENGE_SCHEDULED; } diff --git a/src/PDPVerifierLayout.json b/src/PDPVerifierLayout.json index a6ec5e6d..2440ab17 100644 --- a/src/PDPVerifierLayout.json +++ b/src/PDPVerifierLayout.json @@ -1,6 +1,6 @@ [ { - "label": "challengeFinality", + "label": "deprecatedChallengeFinality", "offset": 0, "slot": "0", "type": { @@ -409,5 +409,45 @@ ], "numberOfBytes": "32" } + }, + { + "label": "cleanupDeposit", + "offset": 0, + "slot": "17", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + }, + { + "label": "cleanupModeEpoch", + "offset": 0, + "slot": "18", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } } ] diff --git a/src/PDPVerifierLayout.sol b/src/PDPVerifierLayout.sol index fc203918..81a17396 100644 --- a/src/PDPVerifierLayout.sol +++ b/src/PDPVerifierLayout.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.20; // This file is a generated binding and any changes will be lost. // Generated with tools/generate_storage_layout.sh -bytes32 constant CHALLENGE_FINALITY_SLOT = bytes32(uint256(0)); +bytes32 constant DEPRECATED_CHALLENGE_FINALITY_SLOT = bytes32(uint256(0)); bytes32 constant NEXT_DATA_SET_ID_SLOT = bytes32(uint256(1)); bytes32 constant PIECE_CIDS_SLOT = bytes32(uint256(2)); bytes32 constant PIECE_LEAF_COUNTS_SLOT = bytes32(uint256(3)); @@ -22,3 +22,5 @@ bytes32 constant DATA_SET_PROPOSED_STORAGE_PROVIDER_SLOT = bytes32(uint256(13)); bytes32 constant DATA_SET_LAST_PROVEN_EPOCH_SLOT = bytes32(uint256(14)); bytes32 constant FEE_STATUS_SLOT = bytes32(uint256(15)); bytes32 constant NEXT_UPGRADE_SLOT = bytes32(uint256(16)); +bytes32 constant CLEANUP_DEPOSIT_SLOT = bytes32(uint256(17)); +bytes32 constant CLEANUP_MODE_EPOCH_SLOT = bytes32(uint256(18)); diff --git a/src/interfaces/IPDPVerifier.sol b/src/interfaces/IPDPVerifier.sol index e339da4e..528d8478 100644 --- a/src/interfaces/IPDPVerifier.sol +++ b/src/interfaces/IPDPVerifier.sol @@ -30,6 +30,7 @@ interface IPDPVerifier is IPDPEvents { function claimDataSetStorageProvider(uint256 setId, bytes calldata extraData) external; function createDataSet(address listenerAddr, bytes calldata extraData) external payable returns (uint256); function deleteDataSet(uint256 setId, bytes calldata extraData) external; + function cleanupPieces(uint256 setId, uint256 maxPieces) external returns (bool done); function addPieces(uint256 setId, address listenerAddr, Cids.Cid[] calldata pieceData, bytes calldata extraData) external payable @@ -45,9 +46,6 @@ interface IPDPVerifier is IPDPEvents { // Fee view: returns the current effective fee per TiB function feePerTiB() external view returns (uint96); - // USDFC sybil fee amount - function USDFC_SYBIL_FEE() external view returns (uint256); - - // FIL sybil fee amount - function FIL_SYBIL_FEE() external pure returns (uint256); + // FIL cleanup deposit amount held per data set + function FIL_CLEANUP_DEPOSIT() external pure returns (uint256); } diff --git a/test/CleanupPieces.t.sol b/test/CleanupPieces.t.sol new file mode 100644 index 00000000..039e9559 --- /dev/null +++ b/test/CleanupPieces.t.sol @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT +pragma solidity ^0.8.13; + +import {MockFVMTest} from "fvm-solidity/mocks/MockFVMTest.sol"; +import {Cids} from "../src/Cids.sol"; +import {PDPVerifier, PDPListener} from "../src/PDPVerifier.sol"; +import {MyERC1967Proxy} from "../src/ERC1967Proxy.sol"; +import {PDPFees} from "../src/Fees.sol"; +import {PDPRecordKeeper} from "../src/SimplePDPService.sol"; +import {PieceHelper} from "./PieceHelper.t.sol"; +import {NEW_DATA_SET_SENTINEL} from "../src/PDPVerifier.sol"; +import {PIECE_CIDS_SLOT, PIECE_LEAF_COUNTS_SLOT, SUM_TREE_COUNTS_SLOT} from "../src/PDPVerifierLayout.sol"; + +contract TestListener is PDPListener, PDPRecordKeeper { + function storageProviderChanged(uint256, address, address, bytes calldata) external override {} + + function dataSetCreated(uint256 dataSetId, address creator, bytes calldata extraData) external override { + receiveDataSetEvent(dataSetId, PDPRecordKeeper.OperationType.CREATE, abi.encode(creator, extraData)); + } + + function dataSetDeleted(uint256 dataSetId, uint256 deletedLeafCount, bytes calldata) external override { + receiveDataSetEvent(dataSetId, PDPRecordKeeper.OperationType.DELETE, abi.encode(deletedLeafCount)); + } + + function piecesAdded(uint256 dataSetId, uint256 firstAdded, Cids.Cid[] calldata pieceData, bytes calldata) + external + override + { + receiveDataSetEvent(dataSetId, PDPRecordKeeper.OperationType.ADD, abi.encode(firstAdded, pieceData)); + } + + function piecesScheduledRemove(uint256 dataSetId, uint256[] calldata pieceIds, bytes calldata) external override { + receiveDataSetEvent(dataSetId, PDPRecordKeeper.OperationType.REMOVE_SCHEDULED, abi.encode(pieceIds)); + } + + function possessionProven(uint256 dataSetId, uint256 challengedLeafCount, uint256 seed, uint256 challengeCount) + external + override + { + receiveDataSetEvent( + dataSetId, + PDPRecordKeeper.OperationType.PROVE_POSSESSION, + abi.encode(challengedLeafCount, seed, challengeCount) + ); + } + + function nextProvingPeriod(uint256 dataSetId, uint256 challengeEpoch, uint256 leafCount, bytes calldata) + external + override + { + receiveDataSetEvent( + dataSetId, PDPRecordKeeper.OperationType.NEXT_PROVING_PERIOD, abi.encode(challengeEpoch, leafCount) + ); + } +} + +contract PDPVerifierCleanupTest is MockFVMTest, PieceHelper { + uint256 constant CHALLENGE_FINALITY_DELAY = 2; + + PDPVerifier pdpVerifier; + TestListener listener; + bytes empty = new bytes(0); + + receive() external payable {} + + function setUp() public override { + super.setUp(); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, CHALLENGE_FINALITY_DELAY); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); + MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); + pdpVerifier = PDPVerifier(address(proxy)); + listener = new TestListener(); + } + + function _createAndPopulate(uint256 numPieces) internal returns (uint256 setId) { + setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( + NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) + ); + if (numPieces > 0) { + Cids.Cid[] memory pieces = new Cids.Cid[](numPieces); + for (uint256 i = 0; i < numPieces; i++) { + pieces[i] = makeSamplePiece(2); + } + pdpVerifier.addPieces(setId, address(0), pieces, empty); + } + } + + // Asserts that pieceCids, pieceLeafCounts, and sumTreeCounts are all zero for + // every piece slot [0, numPieces) on the given data set. Reads raw storage via + // vm.load so that the check works even after the data set is no longer live. + function assertPieceSlotsCleared(uint256 setId, uint256 numPieces) internal view { + // Inner mapping root keyed by setId — shared across all pieces. + bytes32 cidRoot = keccak256(abi.encode(setId, PIECE_CIDS_SLOT)); + bytes32 leafRoot = keccak256(abi.encode(setId, PIECE_LEAF_COUNTS_SLOT)); + bytes32 sumRoot = keccak256(abi.encode(setId, SUM_TREE_COUNTS_SLOT)); + + for (uint256 pieceId = 0; pieceId < numPieces; pieceId++) { + // pieceCids stores a `bytes` value; for a 39-byte CID the header holds length*2+1=79. + // After delete, the header must be zero. + bytes32 cidHeaderSlot = keccak256(abi.encode(pieceId, cidRoot)); + assertEq(vm.load(address(pdpVerifier), cidHeaderSlot), bytes32(0), "pieceCids header not cleared"); + // For a 39-byte CID (long encoding), the payload occupies 2 slots at keccak256(headerSlot). + // ceil(39/32) = 2, so both data slots must be zero for storage to be fully reclaimed. + bytes32 cidDataSlot = keccak256(abi.encodePacked(cidHeaderSlot)); + assertEq(vm.load(address(pdpVerifier), cidDataSlot), bytes32(0), "pieceCids data slot 0 not cleared"); + assertEq( + vm.load(address(pdpVerifier), bytes32(uint256(cidDataSlot) + 1)), + bytes32(0), + "pieceCids data slot 1 not cleared" + ); + assertEq( + vm.load(address(pdpVerifier), keccak256(abi.encode(pieceId, leafRoot))), + bytes32(0), + "pieceLeafCounts not cleared" + ); + assertEq( + vm.load(address(pdpVerifier), keccak256(abi.encode(pieceId, sumRoot))), + bytes32(0), + "sumTreeCounts not cleared" + ); + } + } + + // --- cleanupPieces basics --- + + function testCleanupPiecesIncrementalBatches() public { + uint256 setId = _createAndPopulate(3); + pdpVerifier.deleteDataSet(setId, empty); + assertFalse(pdpVerifier.dataSetLive(setId)); + + // clean 2 of 3 + bool done = pdpVerifier.cleanupPieces(setId, 2); + assertFalse(done, "should not be done after 2 of 3"); + + // clean last piece + uint256 balanceBefore = address(this).balance; + done = pdpVerifier.cleanupPieces(setId, 1); + assertTrue(done, "should be done after last piece"); + assertEq(address(this).balance - balanceBefore, PDPFees.cleanupDeposit(), "deposit returned on completion"); + assertPieceSlotsCleared(setId, 3); + } + + function testCleanupPiecesInOneCall() public { + uint256 setId = _createAndPopulate(2); + pdpVerifier.deleteDataSet(setId, empty); + + uint256 balanceBefore = address(this).balance; + bool done = pdpVerifier.cleanupPieces(setId, 100); + assertTrue(done); + assertEq(address(this).balance - balanceBefore, PDPFees.cleanupDeposit()); + assertPieceSlotsCleared(setId, 2); + } + + function testCleanupPiecesDepositNotPaidUntilComplete() public { + uint256 setId = _createAndPopulate(3); + pdpVerifier.deleteDataSet(setId, empty); + + uint256 balanceBefore = address(this).balance; + pdpVerifier.cleanupPieces(setId, 2); + assertEq(address(this).balance, balanceBefore, "no deposit paid for partial cleanup"); + } + + // --- zero-piece data sets --- + + function testZeroPieceDataSetFinalizesAtDelete() public { + uint256 balanceBefore = address(this).balance; + uint256 setId = _createAndPopulate(0); // holds cleanupDeposit + + pdpVerifier.deleteDataSet(setId, empty); // returns cleanupDeposit immediately + + // Net cost is zero; the cleanup deposit was returned at delete time + assertEq(balanceBefore, address(this).balance, "net cost is zero"); + assertFalse(pdpVerifier.dataSetLive(setId)); + } + + function testZeroPieceDataSetCleanupPiecesReverts() public { + uint256 setId = _createAndPopulate(0); + pdpVerifier.deleteDataSet(setId, empty); + + // After full finalization, cleanupPieces should revert + vm.expectRevert(PDPVerifier.DataSetNotInCleanupMode.selector); + pdpVerifier.cleanupPieces(setId, 10); + } + + // --- caller gating --- + + function testOnlySpCanCleanWithinInactivityWindow() public { + uint256 setId = _createAndPopulate(1); + pdpVerifier.deleteDataSet(setId, empty); + + // block.number (1) <= cleanupModeEpoch + INACTIVITY_WINDOW, so only SP can call + address notSp = address(0xBEEF); + vm.prank(notSp); + vm.expectRevert(PDPVerifier.OnlyStorageProviderCanCleanupPieces.selector); + pdpVerifier.cleanupPieces(setId, 10); + + // SP succeeds + bool done = pdpVerifier.cleanupPieces(setId, 10); + assertTrue(done); + assertPieceSlotsCleared(setId, 1); + } + + function testCleanupPermissionlessAfterInactivityWindow() public { + uint256 setId = _createAndPopulate(1); + pdpVerifier.deleteDataSet(setId, empty); + + vm.roll(block.number + pdpVerifier.INACTIVITY_WINDOW() + 1); + + address anyone = address(0xCAFE); + vm.deal(anyone, 10 ether); + uint256 balanceBefore = anyone.balance; + + vm.prank(anyone); + bool done = pdpVerifier.cleanupPieces(setId, 10); + assertTrue(done); + assertEq(anyone.balance - balanceBefore, PDPFees.cleanupDeposit(), "third party receives deposit"); + assertPieceSlotsCleared(setId, 1); + } + + function testPermissionlessDeleteAfterInactivity() public { + uint256 setId = _createAndPopulate(1); + + // Roll past the inactivity window from the dataset creation block + vm.roll(block.number + pdpVerifier.INACTIVITY_WINDOW() + 1); + + address anyone = address(0xDEAD); + vm.prank(anyone); + pdpVerifier.deleteDataSet(setId, empty); + assertFalse(pdpVerifier.dataSetLive(setId)); + } + + function testOnlySpCanDeleteWithinInactivityWindow() public { + uint256 setId = _createAndPopulate(1); + + // block.number (1) <= lastProvenEpoch(0) + INACTIVITY_WINDOW, so only SP can delete + address notSp = address(0xBEEF); + vm.prank(notSp); + vm.expectRevert(PDPVerifier.OnlyStorageProviderCanDelete.selector); + pdpVerifier.deleteDataSet(setId, empty); + } + + // --- guard conditions --- + + function testCleanupPiecesRequiresCleanupMode() public { + uint256 setId = _createAndPopulate(1); + // data set is still live — cleanupPieces must revert + vm.expectRevert(PDPVerifier.DataSetNotInCleanupMode.selector); + pdpVerifier.cleanupPieces(setId, 10); + } + + function testCleanupPiecesFailsOnLiveZeroPieceDataSet() public { + uint256 setId = _createAndPopulate(0); + // live with no pieces — still not in cleanup mode + vm.expectRevert(PDPVerifier.DataSetNotInCleanupMode.selector); + pdpVerifier.cleanupPieces(setId, 10); + } + + function testCleanupPiecesFailsOnNonExistentDataSet() public { + uint256 nonExistent = pdpVerifier.getNextDataSetId(); + vm.expectRevert(PDPVerifier.DataSetNotInCleanupMode.selector); + pdpVerifier.cleanupPieces(nonExistent, 10); + } + + function testCleanupPiecesMaxPiecesZeroReverts() public { + uint256 setId = _createAndPopulate(1); + pdpVerifier.deleteDataSet(setId, empty); + + vm.expectRevert(PDPVerifier.MaxPiecesMustBePositive.selector); + pdpVerifier.cleanupPieces(setId, 0); + } + + function testDeleteAlreadyInCleanupReverts() public { + uint256 setId = _createAndPopulate(1); + pdpVerifier.deleteDataSet(setId, empty); + + vm.expectRevert(PDPVerifier.DataSetAlreadyInCleanup.selector); + pdpVerifier.deleteDataSet(setId, empty); + } + + // --- scheduled removals cleanup --- + + function testCleanupWithUnprocessedScheduledRemovals() public { + uint256 setId = _createAndPopulate(2); + + // Schedule removal without processing via nextProvingPeriod + uint256[] memory toRemove = new uint256[](1); + toRemove[0] = 0; + pdpVerifier.schedulePieceDeletions(setId, toRemove, empty); + + pdpVerifier.deleteDataSet(setId, empty); + + // Should finalize cleanly even with unprocessed scheduled removals + bool done = pdpVerifier.cleanupPieces(setId, 100); + assertTrue(done); + } +} diff --git a/test/ERC1967Proxy.t.sol b/test/ERC1967Proxy.t.sol index 921c6e72..fef73e53 100644 --- a/test/ERC1967Proxy.t.sol +++ b/test/ERC1967Proxy.t.sol @@ -8,6 +8,7 @@ import {MyERC1967Proxy} from "../src/ERC1967Proxy.sol"; contract ERC1967ProxyTest is Test { PDPVerifier public implementation; + PDPVerifier public newImplementation; PDPVerifier public proxy; address owner = address(0x123); @@ -15,13 +16,11 @@ contract ERC1967ProxyTest is Test { // Set owner for testing vm.startPrank(owner); // Deploy implementation contract - implementation = new PDPVerifier(1, address(0), 1, address(0)); + implementation = new PDPVerifier(1, 150); + newImplementation = new PDPVerifier(2, 150); // Deploy proxy pointing to implementation - bytes memory initData = abi.encodeWithSelector( - PDPVerifier.initialize.selector, - uint256(150) // challengeFinality - ); + bytes memory initData = abi.encodeWithSelector(PDPVerifier.initialize.selector); ERC1967Proxy proxyContract = new MyERC1967Proxy(address(implementation), initData); @@ -42,9 +41,6 @@ contract ERC1967ProxyTest is Test { function testUpgradeImplementation() public { assertImplementationEquals(address(implementation)); - // Deploy new implementation - PDPVerifier newImplementation = new PDPVerifier(2, address(0), 1, address(0)); - // Announce upgrade first (required by new upgrade pattern) PDPVerifier.PlannedUpgrade memory plan; plan.nextImplementation = address(newImplementation); @@ -64,8 +60,6 @@ contract ERC1967ProxyTest is Test { } function testUpgradeFromNonOwnerNoGood() public { - PDPVerifier newImplementation = new PDPVerifier(2, address(0), 1, address(0)); - // Announce upgrade first (as owner) PDPVerifier.PlannedUpgrade memory plan; plan.nextImplementation = address(newImplementation); diff --git a/test/Fees.t.sol b/test/Fees.t.sol index 8e6804fa..68192245 100644 --- a/test/Fees.t.sol +++ b/test/Fees.t.sol @@ -41,8 +41,7 @@ contract PDPFeesTest is Test { assertEq(PDPFees.DEFAULT_FEE_PER_TIB, expectedFee, "DEFAULT_FEE_PER_TIB should be 0.00023 FIL"); } - function testSybilFee() public pure { - uint256 fee = PDPFees.sybilFee(); - assertEq(fee, PDPFees.SYBIL_FEE, "Sybil fee should match the constant"); + function testCleanupDepositConstant() public pure { + assertEq(PDPFees.cleanupDeposit(), 0.1 ether, "FIL_CLEANUP_DEPOSIT should be 0.1 FIL"); } } diff --git a/test/PDPVerifier.t.sol b/test/PDPVerifier.t.sol index 805b4d9c..6b82a8cc 100644 --- a/test/PDPVerifier.t.sol +++ b/test/PDPVerifier.t.sol @@ -14,7 +14,7 @@ import {IPDPTypes} from "../src/interfaces/IPDPTypes.sol"; import {IPDPEvents} from "../src/interfaces/IPDPEvents.sol"; import {PieceHelper} from "./PieceHelper.t.sol"; import {ProofBuilderHelper} from "./ProofBuilderHelper.t.sol"; -import {NEW_DATA_SET_SENTINEL} from "../src/PDPVerifier.sol"; +import {NEW_DATA_SET_SENTINEL, NO_CHALLENGE_SCHEDULED} from "../src/PDPVerifier.sol"; contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { TestingRecordKeeperService listener; @@ -23,9 +23,9 @@ contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, address(0), 1, address(0)); uint256 challengeFinality = 2; - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, challengeFinality); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, challengeFinality); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = PDPVerifier(address(proxy)); listener = new TestingRecordKeeperService(); @@ -37,7 +37,7 @@ contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { vm.expectEmit(true, true, false, false); emit IPDPEvents.DataSetCreated(1, address(this)); - uint256 setId = pdpVerifier.createDataSet{value: PDPFees.sybilFee()}(address(listener), empty); + uint256 setId = pdpVerifier.createDataSet{value: PDPFees.cleanupDeposit()}(address(listener), empty); assertEq(setId, 1, "First data set ID should be 1"); assertEq(pdpVerifier.getDataSetLeafCount(setId), 0, "Data set leaf count should be 0"); @@ -64,7 +64,7 @@ contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { function testDeleteDataSet() public { vm.expectEmit(true, true, false, false); emit IPDPEvents.DataSetCreated(1, address(this)); - uint256 setId = pdpVerifier.createDataSet{value: PDPFees.sybilFee()}(address(listener), empty); + uint256 setId = pdpVerifier.createDataSet{value: PDPFees.cleanupDeposit()}(address(listener), empty); vm.expectEmit(true, true, false, false); emit IPDPEvents.DataSetDeleted(setId, 0); pdpVerifier.deleteDataSet(setId, empty); @@ -75,12 +75,12 @@ contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { function testOnlyStorageProviderCanDeleteDataSet() public { vm.expectEmit(true, true, false, false); emit IPDPEvents.DataSetCreated(1, address(this)); - uint256 setId = pdpVerifier.createDataSet{value: PDPFees.sybilFee()}(address(listener), empty); + uint256 setId = pdpVerifier.createDataSet{value: PDPFees.cleanupDeposit()}(address(listener), empty); // Create a new address to act as a non-storage-provider address nonStorageProvider = address(0x1234); // Expect revert when non-storage-provider tries to delete the data set vm.prank(nonStorageProvider); - vm.expectRevert("Only the storage provider can delete data sets"); + vm.expectRevert(PDPVerifier.OnlyStorageProviderCanDelete.selector); pdpVerifier.deleteDataSet(setId, empty); // Now verify the storage provider can delete the data set @@ -94,7 +94,7 @@ contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { // TODO: once we have addPieces we should test deletion of a non empty data set function testCannotDeleteNonExistentDataSet() public { // Test with data set ID 0 (which is never valid since IDs start from 1) - vm.expectRevert("Only the storage provider can delete data sets"); + vm.expectRevert(PDPVerifier.DataSetNotLive.selector); pdpVerifier.deleteDataSet(0, empty); // Test with a data set ID that hasn't been created yet @@ -105,12 +105,12 @@ contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { function testMethodsOnDeletedDataSetFails() public { vm.expectEmit(true, true, false, false); emit IPDPEvents.DataSetCreated(1, address(this)); - uint256 setId = pdpVerifier.createDataSet{value: PDPFees.sybilFee()}(address(listener), empty); + uint256 setId = pdpVerifier.createDataSet{value: PDPFees.cleanupDeposit()}(address(listener), empty); vm.expectEmit(true, true, false, false); emit IPDPEvents.DataSetDeleted(setId, 0); pdpVerifier.deleteDataSet(setId, empty); - vm.expectRevert("Only the storage provider can delete data sets"); + vm.expectRevert(PDPVerifier.DataSetNotLive.selector); pdpVerifier.deleteDataSet(setId, empty); vm.expectRevert("Data set not live"); pdpVerifier.getDataSetStorageProvider(setId); @@ -131,10 +131,10 @@ contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { function testGetDataSetID() public { vm.expectEmit(true, true, false, false); emit IPDPEvents.DataSetCreated(1, address(this)); - pdpVerifier.createDataSet{value: PDPFees.sybilFee()}(address(listener), empty); + pdpVerifier.createDataSet{value: PDPFees.cleanupDeposit()}(address(listener), empty); vm.expectEmit(true, true, false, false); emit IPDPEvents.DataSetCreated(2, address(this)); - pdpVerifier.createDataSet{value: PDPFees.sybilFee()}(address(listener), empty); + pdpVerifier.createDataSet{value: PDPFees.cleanupDeposit()}(address(listener), empty); assertEq(3, pdpVerifier.getNextDataSetId(), "Next data set ID should be 3"); assertEq(3, pdpVerifier.getNextDataSetId(), "Next data set ID should be 3"); } @@ -145,30 +145,30 @@ contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { // Test that data set IDs start from 1, not 0 assertEq(pdpVerifier.getNextDataSetId(), 1, "Next data set ID should start at 1"); - uint256 firstSetId = pdpVerifier.createDataSet{value: PDPFees.sybilFee()}(address(listener), empty); + uint256 firstSetId = pdpVerifier.createDataSet{value: PDPFees.cleanupDeposit()}(address(listener), empty); assertEq(firstSetId, 1, "First data set ID should be 1, not 0"); - uint256 secondSetId = pdpVerifier.createDataSet{value: PDPFees.sybilFee()}(address(listener), empty); + uint256 secondSetId = pdpVerifier.createDataSet{value: PDPFees.cleanupDeposit()}(address(listener), empty); assertEq(secondSetId, 2, "Second data set ID should be 2"); assertEq(pdpVerifier.getNextDataSetId(), 3, "Next data set ID should be 3 after creating two data sets"); } function testCreateDataSetFeeHandling() public { - uint256 sybilFee = PDPFees.sybilFee(); + uint256 required = PDPFees.cleanupDeposit(); - // Test 1: Fails when sending not enough for sybil fee - vm.expectRevert("sybil fee not met"); - pdpVerifier.createDataSet{value: sybilFee - 1}(address(listener), empty); + // Test 1: Fails when sending not enough for cleanup deposit + vm.expectRevert(PDPVerifier.CleanupDepositRequired.selector); + pdpVerifier.createDataSet{value: required - 1}(address(listener), empty); - // Test 2: Returns funds over the sybil fee back to the sender + // Test 2: Returns funds over the required amount back to the sender uint256 excessAmount = 1 ether; uint256 initialBalance = address(this).balance; - uint256 setId = pdpVerifier.createDataSet{value: sybilFee + excessAmount}(address(listener), empty); + uint256 setId = pdpVerifier.createDataSet{value: required + excessAmount}(address(listener), empty); uint256 finalBalance = address(this).balance; - uint256 refundedAmount = finalBalance - (initialBalance - sybilFee - excessAmount); + uint256 refundedAmount = finalBalance - (initialBalance - required - excessAmount); assertEq(refundedAmount, excessAmount, "Excess amount should be refunded"); // Additional checks to ensure the data set was created correctly @@ -183,7 +183,7 @@ contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { } function testCombinedCreateDataSetAndAddPieces() public { - uint256 sybilFee = PDPFees.sybilFee(); + uint256 createFee = PDPFees.cleanupDeposit(); bytes memory combinedExtraData = abi.encode(empty, empty); Cids.Cid[] memory pieces = new Cids.Cid[](2); @@ -200,7 +200,7 @@ contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { emit IPDPEvents.PiecesAdded(1, expectedPieceIds, pieces); uint256 firstAdded = - pdpVerifier.addPieces{value: sybilFee}(NEW_DATA_SET_SENTINEL, address(listener), pieces, combinedExtraData); + pdpVerifier.addPieces{value: createFee}(NEW_DATA_SET_SENTINEL, address(listener), pieces, combinedExtraData); // Verify the data set was created correctly assertEq(firstAdded, 1, "First piece ID should be 1"); @@ -218,12 +218,12 @@ contract PDPVerifierDataSetCreateDeleteTest is MockFVMTest, PieceHelper { function testNewDataSetSentinelValue() public { assertEq(NEW_DATA_SET_SENTINEL, 0, "Sentinel value should be 0"); - uint256 sybilFee = PDPFees.sybilFee(); + uint256 createFee = PDPFees.cleanupDeposit(); bytes memory combinedExtraData = abi.encode(empty, empty); Cids.Cid[] memory pieces = new Cids.Cid[](0); uint256 firstAdded = - pdpVerifier.addPieces{value: sybilFee}(NEW_DATA_SET_SENTINEL, address(listener), pieces, combinedExtraData); + pdpVerifier.addPieces{value: createFee}(NEW_DATA_SET_SENTINEL, address(listener), pieces, combinedExtraData); assertEq(firstAdded, 1, "First piece ID should be 1"); assertEq(pdpVerifier.getDataSetLeafCount(firstAdded), 0, "Data set leaf count should be 0"); @@ -240,8 +240,8 @@ contract PDPVerifierStorageProviderTest is MockFVMTest, PieceHelper { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, address(0), 1, address(0)); - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, 2); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, 2); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = PDPVerifier(address(proxy)); listener = new TestingRecordKeeperService(); @@ -252,7 +252,7 @@ contract PDPVerifierStorageProviderTest is MockFVMTest, PieceHelper { } function testStorageProviderTransfer() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); pdpVerifier.proposeDataSetStorageProvider(setId, nextStorageProvider); @@ -280,7 +280,7 @@ contract PDPVerifierStorageProviderTest is MockFVMTest, PieceHelper { } function testStorageProviderProposalReset() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); pdpVerifier.proposeDataSetStorageProvider(setId, nextStorageProvider); @@ -294,7 +294,7 @@ contract PDPVerifierStorageProviderTest is MockFVMTest, PieceHelper { } function testStorageProviderPermissionsRequired() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); vm.prank(nonStorageProvider); @@ -315,7 +315,7 @@ contract PDPVerifierStorageProviderTest is MockFVMTest, PieceHelper { } function testScheduleRemovePiecesOnlyStorageProvider() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); Cids.Cid[] memory pieceDataArray = new Cids.Cid[](1); @@ -338,10 +338,12 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { TestingRecordKeeperService listener; bytes empty = new bytes(0); + receive() external payable {} + function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, address(0), 1, address(0)); - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, CHALLENGE_FINALITY_DELAY); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, CHALLENGE_FINALITY_DELAY); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = PDPVerifier(address(proxy)); listener = new TestingRecordKeeperService(); @@ -350,7 +352,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { function testAddPiece() public { vm.expectEmit(true, true, false, false); emit IPDPEvents.DataSetCreated(1, address(this)); - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -380,7 +382,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { } function testAddPiecesToExistingDataSetWithFee() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -407,7 +409,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { } function testAddPiecesToExistingDataSetWrongStorageProvider() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -425,7 +427,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { function testAddMultiplePieces() public { vm.expectEmit(true, true, false, false); emit IPDPEvents.DataSetCreated(1, address(this)); - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); Cids.Cid[] memory pieces = new Cids.Cid[](2); @@ -466,7 +468,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { } function testAddBadPiece() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); Cids.Cid[] memory pieces = new Cids.Cid[](1); @@ -494,7 +496,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { function testAddBadPiecesBatched() public { // Add one bad piece, message fails on bad index - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); Cids.Cid[] memory pieces = new Cids.Cid[](4); @@ -514,13 +516,13 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { function testRemovePiece() public { // Add one piece - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); Cids.Cid[] memory pieces = new Cids.Cid[](1); pieces[0] = makeSamplePiece(2); pdpVerifier.addPieces(setId, address(0), pieces, empty); - assertEq(pdpVerifier.getNextChallengeEpoch(setId), pdpVerifier.NO_CHALLENGE_SCHEDULED()); // Not updated on first add anymore + assertEq(pdpVerifier.getNextChallengeEpoch(setId), NO_CHALLENGE_SCHEDULED); // Not updated on first add anymore pdpVerifier.nextProvingPeriod(setId, vm.getBlockNumber() + CHALLENGE_FINALITY_DELAY, empty); assertEq(pdpVerifier.getNextChallengeEpoch(setId), vm.getBlockNumber() + CHALLENGE_FINALITY_DELAY); @@ -533,7 +535,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { emit IPDPEvents.PiecesRemoved(setId, toRemove); pdpVerifier.nextProvingPeriod(setId, vm.getBlockNumber() + CHALLENGE_FINALITY_DELAY, empty); // flush - assertEq(pdpVerifier.getNextChallengeEpoch(setId), pdpVerifier.NO_CHALLENGE_SCHEDULED()); + assertEq(pdpVerifier.getNextChallengeEpoch(setId), NO_CHALLENGE_SCHEDULED); assertEq(pdpVerifier.pieceLive(setId, 0), false); assertEq(pdpVerifier.getNextPieceId(setId), 1); assertEq(pdpVerifier.getDataSetLeafCount(setId), 0); @@ -544,7 +546,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { function testCannotScheduleRemovalOnNonLiveDataSet() public { // Create a data set - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -564,7 +566,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { } function testRemovePieceBatch() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); Cids.Cid[] memory pieces = new Cids.Cid[](3); @@ -600,7 +602,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { } function testSchedulePieceDeletionsDuplicatePrevention() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); Cids.Cid[] memory pieces = new Cids.Cid[](3); @@ -642,7 +644,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { } function testMappingClearedAfterRemoval() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); Cids.Cid[] memory pieces = new Cids.Cid[](3); @@ -682,7 +684,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { function testBitmapWithLargePieceIds() public { // Setup: Create dataset and add many pieces - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -716,7 +718,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { } function testRemoveFuturePieces() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); Cids.Cid[] memory pieces = new Cids.Cid[](1); @@ -755,7 +757,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { function testOnlyStorageProviderCanModifyDataSet() public { // Setup a piece we can add - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); Cids.Cid[] memory pieces = new Cids.Cid[](1); @@ -772,7 +774,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { // Try to delete data set as non-storage-provider vm.prank(nonStorageProvider); - vm.expectRevert("Only the storage provider can delete data sets"); + vm.expectRevert(PDPVerifier.OnlyStorageProviderCanDelete.selector); pdpVerifier.deleteDataSet(setId, empty); // Try to schedule removals as non-storage-provider @@ -796,7 +798,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { } function testNextProvingPeriodChallengeEpochTooSoon() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); // Add a piece to the data set (otherwise nextProvingPeriod fails waiting for leaves) @@ -812,7 +814,11 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { uint256 tooSoonEpoch = currentBlock + CHALLENGE_FINALITY_DELAY - 1; // Expect revert with the specific error message - vm.expectRevert("challenge epoch must be at least challengeFinality epochs in the future"); + vm.expectRevert( + abi.encodeWithSelector( + PDPVerifier.InsufficientChallengeDelay.selector, CHALLENGE_FINALITY_DELAY - 1, CHALLENGE_FINALITY_DELAY + ) + ); pdpVerifier.nextProvingPeriod(setId, tooSoonEpoch, ""); // Set challenge epoch to exactly challengeFinality epochs in the future @@ -826,11 +832,31 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { assertEq(pdpVerifier.getNextChallengeEpoch(setId), validEpoch); } + function testNextProvingPeriodChallengeEpochBeforeCurrentBlock() public { + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( + NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) + ); + Cids.Cid[] memory pieces = new Cids.Cid[](1); + pieces[0] = makeSamplePiece(2); + pdpVerifier.addPieces(setId, address(0), pieces, empty); + + vm.roll(10); + uint256 currentBlock = vm.getBlockNumber(); + uint256 pastEpoch = currentBlock - 1; + + uint256 maxFinality = pdpVerifier.INACTIVITY_WINDOW() / 2; + + vm.expectRevert( + abi.encodeWithSelector(PDPVerifier.ExcessiveChallengeDelay.selector, type(uint256).max, maxFinality) + ); + pdpVerifier.nextProvingPeriod(setId, pastEpoch, ""); + } + function testNextProvingPeriodWithNoData() public { // Get the NO_CHALLENGE_SCHEDULED constant value for clarity - uint256 noChallenge = pdpVerifier.NO_CHALLENGE_SCHEDULED(); + uint256 noChallenge = NO_CHALLENGE_SCHEDULED; - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -852,7 +878,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { function testNextProvingPeriodRevertsOnEmptyDataSet() public { // Create a new data set - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -864,7 +890,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { function testEmitDataSetEmptyEvent() public { // Create a data set with one piece - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -887,7 +913,7 @@ contract PDPVerifierDataSetMutateTest is MockFVMTest, PieceHelper { // Verify the data set is indeed empty assertEq(pdpVerifier.getDataSetLeafCount(setId), 0); assertEq(pdpVerifier.getNextChallengeEpoch(setId), 0); - assertEq(pdpVerifier.getDataSetLastProvenEpoch(setId), 0); + assertEq(pdpVerifier.getDataSetLastProvenEpoch(setId), block.number); } } @@ -898,9 +924,9 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, address(0), 1, address(0)); uint256 challengeFinality = 2; - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, challengeFinality); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, challengeFinality); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = PDPVerifier(address(proxy)); listener = new TestingRecordKeeperService(); @@ -908,7 +934,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { function testGetActivePiecesEmpty() public { // Create empty data set and test - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -923,7 +949,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { } function testGetActivePiecesPagination() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -973,7 +999,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { } function testGetActivePiecesWithDeleted() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -1012,7 +1038,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { } function testGetActivePiecesEdgeCases() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -1055,7 +1081,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { } function testGetActivePiecesHasMore() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -1084,7 +1110,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { } function testGetActivePiecesLargeSet() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -1134,7 +1160,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { } function testGetActivePiecesByCursorBasic() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -1163,7 +1189,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { } function testGetActivePiecesByCursorWithDeleted() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -1197,7 +1223,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { } function testGetActivePiecesByCursorBeyondRange() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -1217,7 +1243,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { } function testGetActivePiecesByCursorEmpty() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -1235,7 +1261,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper { } function testGetActivePiecesByCursorZeroLimit() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -1292,7 +1318,7 @@ contract TestingRecordKeeperService is PDPListener, PDPRecordKeeper { } contract SumTreeInternalTestPDPVerifier is PDPVerifier { - constructor() PDPVerifier(1, address(0), 1, address(0)) {} + constructor(uint256 _challengeFinality) PDPVerifier(1, _challengeFinality) {} function getTestHeightFromIndex(uint256 index) public pure returns (uint256) { return heightFromIndex(index); @@ -1308,8 +1334,8 @@ contract SumTreeHeightTest is MockFVMTest { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new SumTreeInternalTestPDPVerifier(); - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, 2); + PDPVerifier pdpVerifierImpl = new SumTreeInternalTestPDPVerifier(2); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = SumTreeInternalTestPDPVerifier(address(proxy)); } @@ -1442,12 +1468,12 @@ contract SumTreeAddTest is MockFVMTest, PieceHelper { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new SumTreeInternalTestPDPVerifier(); - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, CHALLENGE_FINALITY_DELAY); + PDPVerifier pdpVerifierImpl = new SumTreeInternalTestPDPVerifier(CHALLENGE_FINALITY_DELAY); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = SumTreeInternalTestPDPVerifier(address(proxy)); listener = new TestingRecordKeeperService(); - testSetId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + testSetId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); } @@ -1803,8 +1829,8 @@ contract PDPListenerIntegrationTest is MockFVMTest, PieceHelper { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, address(0), 1, address(0)); - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, CHALLENGE_FINALITY_DELAY); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, CHALLENGE_FINALITY_DELAY); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = PDPVerifier(address(proxy)); badListener = new BadListener(); @@ -1813,12 +1839,12 @@ contract PDPListenerIntegrationTest is MockFVMTest, PieceHelper { function testListenerPropagatesErrors() public { badListener.setBadOperation(PDPRecordKeeper.OperationType.CREATE); vm.expectRevert("Failing operation"); - pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(badListener), new Cids.Cid[](0), abi.encode(empty, empty) ); badListener.setBadOperation(PDPRecordKeeper.OperationType.NONE); - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(badListener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -1893,8 +1919,8 @@ contract PDPVerifierExtraDataTest is MockFVMTest, PieceHelper { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, address(0), 1, address(0)); - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, CHALLENGE_FINALITY_DELAY); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, CHALLENGE_FINALITY_DELAY); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = PDPVerifier(address(proxy)); extraDataListener = new ExtraDataListener(); @@ -1902,7 +1928,7 @@ contract PDPVerifierExtraDataTest is MockFVMTest, PieceHelper { function testExtraDataPropagation() public { // Test CREATE operation - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(extraDataListener), new Cids.Cid[](0), abi.encode(empty, empty) ); assertEq( @@ -1949,8 +1975,8 @@ contract PDPVerifierE2ETest is MockFVMTest, ProofBuilderHelper, PieceHelper { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, address(0), 1, address(0)); - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, CHALLENGE_FINALITY_DELAY); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, CHALLENGE_FINALITY_DELAY); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = PDPVerifier(address(proxy)); listener = new TestingRecordKeeperService(); @@ -1962,7 +1988,7 @@ contract PDPVerifierE2ETest is MockFVMTest, ProofBuilderHelper, PieceHelper { function testCompleteProvingPeriodE2E() public { // Step 1: Create a data set - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); @@ -2089,9 +2115,9 @@ contract PDPVerifierMigrateTest is Test { PDPVerifier pdpVerifier; function setUp() public { - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, 2); - implementation = new PDPVerifier(1, address(0), 1, address(0)); - newImplementation = new PDPVerifier(2, address(0), 1, address(0)); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); + implementation = new PDPVerifier(1, 2); + newImplementation = new PDPVerifier(2, 2); proxy = new MyERC1967Proxy(address(implementation), initializeData); pdpVerifier = PDPVerifier(address(proxy)); } @@ -2102,12 +2128,9 @@ contract PDPVerifierMigrateTest is Test { assertEq(nextImplementation, address(0)); assertEq(afterEpoch, uint96(0)); - // Deploy new implementation - PDPVerifier newImpl = new PDPVerifier(2, address(0), 1, address(0)); - // Announce upgrade PDPVerifier.PlannedUpgrade memory plan; - plan.nextImplementation = address(newImpl); + plan.nextImplementation = address(newImplementation); plan.afterEpoch = uint96(vm.getBlockNumber()) + 2000; vm.expectEmit(false, false, false, true); @@ -2132,7 +2155,7 @@ contract PDPVerifierMigrateTest is Test { // Can upgrade at afterEpoch vm.roll(plan.afterEpoch); vm.expectEmit(false, false, false, true); - emit PDPVerifier.ContractUpgraded(newImpl.VERSION(), plan.nextImplementation); + emit PDPVerifier.ContractUpgraded(newImplementation.VERSION(), plan.nextImplementation); pdpVerifier.upgradeToAndCall(plan.nextImplementation, migrateData); // After upgrade, nextUpgrade should be cleared @@ -2142,9 +2165,8 @@ contract PDPVerifierMigrateTest is Test { } function testAnnouncePlannedUpgradeOnlyOwner() public { - PDPVerifier newImpl = new PDPVerifier(2, address(0), 1, address(0)); PDPVerifier.PlannedUpgrade memory plan; - plan.nextImplementation = address(newImpl); + plan.nextImplementation = address(newImplementation); plan.afterEpoch = uint96(vm.getBlockNumber()) + 2000; // Non-owner cannot announce upgrade @@ -2163,9 +2185,8 @@ contract PDPVerifierMigrateTest is Test { } function testAnnouncePlannedUpgradeInvalidEpoch() public { - PDPVerifier newImpl = new PDPVerifier(2, address(0), 1, address(0)); PDPVerifier.PlannedUpgrade memory plan; - plan.nextImplementation = address(newImpl); + plan.nextImplementation = address(newImplementation); plan.afterEpoch = uint96(vm.getBlockNumber()); // Must be in the future vm.expectRevert(); @@ -2208,8 +2229,8 @@ contract PDPVerifierFeeTest is MockFVMTest, PieceHelper, ProofBuilderHelper { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, address(0), 1, address(0)); - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, CHALLENGE_FINALITY_DELAY); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, CHALLENGE_FINALITY_DELAY); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = PDPVerifier(address(proxy)); vm.fee(1 gwei); @@ -2266,7 +2287,7 @@ contract PDPVerifierFeeTest is MockFVMTest, PieceHelper, ProofBuilderHelper { bytes memory combinedExtra = abi.encode(empty, empty); - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), pieces, combinedExtra ); @@ -2348,8 +2369,8 @@ contract PDPVerifierStorageProviderListenerTest is MockFVMTest { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, address(0), 1, address(0)); - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, 2); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, 2); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = PDPVerifier(address(proxy)); listener = new MockStorageProviderChangedListener(); @@ -2359,7 +2380,7 @@ contract PDPVerifierStorageProviderListenerTest is MockFVMTest { } function testStorageProviderChangedCalledOnStorageProviderTransfer() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); pdpVerifier.proposeDataSetStorageProvider(setId, nextStorageProvider); @@ -2371,7 +2392,7 @@ contract PDPVerifierStorageProviderListenerTest is MockFVMTest { } function testListenerRevertDoesNotRevertMainTx() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); pdpVerifier.proposeDataSetStorageProvider(setId, nextStorageProvider); @@ -2390,13 +2411,13 @@ contract PDPVerifierCIDSearchTest is MockFVMTest, PieceHelper { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, address(0), 1, address(0)); - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, 2); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, 2); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = PDPVerifier(address(proxy)); listener = new TestingRecordKeeperService(); - setId = pdpVerifier.createDataSet{value: PDPFees.sybilFee()}(address(listener), empty); + setId = pdpVerifier.createDataSet{value: PDPFees.cleanupDeposit()}(address(listener), empty); } function testFindPieceIdsByCid_Unique() public { @@ -2495,7 +2516,7 @@ contract PDPVerifierCIDSearchTest is MockFVMTest, PieceHelper { } function testFindPieceIdsByCid_EmptyDataSet() public { - uint256 emptySetId = pdpVerifier.createDataSet{value: PDPFees.sybilFee()}(address(listener), empty); + uint256 emptySetId = pdpVerifier.createDataSet{value: PDPFees.cleanupDeposit()}(address(listener), empty); Cids.Cid memory target = makeSamplePiece(64); uint256[] memory results = pdpVerifier.findPieceIdsByCid(emptySetId, target, 0, 10); assertEq(results.length, 0); @@ -2536,163 +2557,3 @@ contract PDPVerifierCIDSearchTest is MockFVMTest, PieceHelper { assertEq(results[0], 9); } } - -// Mock payments contract that exposes accounts(address,address) returning a funds balance -contract MockPayments { - uint256 public balance; - - function setBalance(uint256 _balance) external { - balance = _balance; - } - - function addBalance(uint256 amount) external { - balance += amount; - } - - // Matches the signature PDPVerifier queries: accounts(address,address) returns (uint256,...) - function accounts(address, address) external view returns (uint256, uint256, uint256, uint256) { - return (balance, 0, 0, 0); - } -} - -// Listener that simulates FWSS depositing USDFC into the payments auction pool during dataSetCreated -contract UsdfcBurningListener is PDPListener, PDPRecordKeeper { - MockPayments public mockPayments; - uint256 public depositAmount; - - constructor(MockPayments _mockPayments) { - mockPayments = _mockPayments; - } - - function setDepositAmount(uint256 amount) external { - depositAmount = amount; - } - - function storageProviderChanged(uint256, address, address, bytes calldata) external override {} - - function dataSetCreated(uint256 dataSetId, address creator, bytes calldata) external override { - // Simulate FWSS depositing the sybil fee into the payments auction pool - if (depositAmount > 0) { - mockPayments.addBalance(depositAmount); - } - receiveDataSetEvent(dataSetId, PDPRecordKeeper.OperationType.CREATE, abi.encode(creator)); - } - - function dataSetDeleted(uint256 dataSetId, uint256 deletedLeafCount, bytes calldata) external override { - receiveDataSetEvent(dataSetId, PDPRecordKeeper.OperationType.DELETE, abi.encode(deletedLeafCount)); - } - - function piecesAdded(uint256 dataSetId, uint256 firstAdded, Cids.Cid[] calldata pieceData, bytes calldata) - external - override - { - receiveDataSetEvent(dataSetId, PDPRecordKeeper.OperationType.ADD, abi.encode(firstAdded, pieceData)); - } - - function piecesScheduledRemove(uint256 dataSetId, uint256[] calldata pieceIds, bytes calldata) external override { - receiveDataSetEvent(dataSetId, PDPRecordKeeper.OperationType.REMOVE_SCHEDULED, abi.encode(pieceIds)); - } - - function possessionProven(uint256 dataSetId, uint256 challengedLeafCount, uint256 seed, uint256 challengeCount) - external - override - { - receiveDataSetEvent( - dataSetId, - PDPRecordKeeper.OperationType.PROVE_POSSESSION, - abi.encode(challengedLeafCount, seed, challengeCount) - ); - } - - function nextProvingPeriod(uint256 dataSetId, uint256 challengeEpoch, uint256 leafCount, bytes calldata) - external - override - { - receiveDataSetEvent( - dataSetId, PDPRecordKeeper.OperationType.NEXT_PROVING_PERIOD, abi.encode(challengeEpoch, leafCount) - ); - } -} - -contract UsdfcSybilFeeTest is MockFVMTest, PieceHelper { - PDPVerifier pdpVerifier; - MockPayments mockPayments; - UsdfcBurningListener listener; - bytes empty = new bytes(0); - - uint256 constant SYBIL_FEE = 0.1e18; - address constant FAKE_USDFC = address(0x1234); - - function setUp() public override { - super.setUp(); - mockPayments = new MockPayments(); - - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, FAKE_USDFC, SYBIL_FEE, address(mockPayments)); - bytes memory initializeData = abi.encodeWithSelector( - PDPVerifier.initialize.selector, - 2 // challengeFinality - ); - MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); - pdpVerifier = PDPVerifier(address(proxy)); - listener = new UsdfcBurningListener(mockPayments); - } - - // When USDFC is not burned and msg.value > 0, FIL burn fallback succeeds - function testFilBurnFallbackWhenNoUsdfc() public { - // Listener deposits nothing — USDFC balance won't increase - listener.setDepositAmount(0); - - uint256 filSybilFee = PDPFees.sybilFee(); - uint256 setId = pdpVerifier.createDataSet{value: filSybilFee}(address(listener), empty); - assertEq(setId, 1); - } - - // When USDFC is insufficient and no FIL sent, tx reverts - function testRevertWhenUsdfcInsufficientAndNoFil() public { - // Listener deposits less than the sybil fee - listener.setDepositAmount(SYBIL_FEE - 1); - - vm.expectRevert(PDPVerifier.UsdfcSybilFeeNotMet.selector); - pdpVerifier.createDataSet(address(listener), empty); - } - - // When USDFC is sufficiently burned, succeeds without FIL - function testSucceedWhenUsdfcSufficientlyBurned() public { - // Listener deposits exactly the sybil fee - listener.setDepositAmount(SYBIL_FEE); - - uint256 setId = pdpVerifier.createDataSet(address(listener), empty); - assertEq(setId, 1); - } - - // Same three scenarios via addPieces (new dataset path) - function testAddPieces_FilBurnFallbackWhenNoUsdfc() public { - listener.setDepositAmount(0); - - uint256 filSybilFee = PDPFees.sybilFee(); - bytes memory combinedExtra = abi.encode(empty, empty); - uint256 setId = pdpVerifier.addPieces{value: filSybilFee}( - NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), combinedExtra - ); - assertEq(setId, 1); - } - - function testAddPieces_RevertWhenUsdfcInsufficientAndNoFil() public { - listener.setDepositAmount(SYBIL_FEE - 1); - - bytes memory combinedExtra = abi.encode(empty, empty); - vm.expectRevert(PDPVerifier.UsdfcSybilFeeNotMet.selector); - pdpVerifier.addPieces(NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), combinedExtra); - } - - function testAddPieces_SucceedWhenUsdfcSufficientlyBurned() public { - listener.setDepositAmount(SYBIL_FEE); - - bytes memory combinedExtra = abi.encode(empty, empty); - uint256 setId = - pdpVerifier.addPieces(NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), combinedExtra); - assertEq(setId, 1); - } - - receive() external payable {} -} diff --git a/test/PDPVerifierProofTest.t.sol b/test/PDPVerifierProofTest.t.sol index 9b630350..fb539771 100644 --- a/test/PDPVerifierProofTest.t.sol +++ b/test/PDPVerifierProofTest.t.sol @@ -22,8 +22,8 @@ contract PDPVerifierProofTest is MockFVMTest, ProofBuilderHelper, PieceHelper { function setUp() public override { super.setUp(); - PDPVerifier pdpVerifierImpl = new PDPVerifier(1, address(0), 1, address(0)); - bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector, CHALLENGE_FINALITY_DELAY); + PDPVerifier pdpVerifierImpl = new PDPVerifier(1, CHALLENGE_FINALITY_DELAY); + bytes memory initializeData = abi.encodeWithSelector(PDPVerifier.initialize.selector); MyERC1967Proxy proxy = new MyERC1967Proxy(address(pdpVerifierImpl), initializeData); pdpVerifier = PDPVerifier(address(proxy)); listener = new TestingRecordKeeperService(); @@ -118,16 +118,21 @@ contract PDPVerifierProofTest is MockFVMTest, ProofBuilderHelper, PieceHelper { } function testDataSetLastProvenEpochOnPieceRemoval() public { - // Create a data set and verify initial lastProvenEpoch is 0 - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + // Create a data set and verify initial lastProvenEpoch is set to the creation block + uint256 creationBlock = vm.getBlockNumber(); + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); - assertEq(pdpVerifier.getDataSetLastProvenEpoch(setId), 0, "Initial lastProvenEpoch should be 0"); + assertEq( + pdpVerifier.getDataSetLastProvenEpoch(setId), + creationBlock, + "Initial lastProvenEpoch should be block.number at creation" + ); // Mock block.number to 2881 uint256 blockNumber = 2881; vm.roll(blockNumber); - // Add a piece and verify lastProvenEpoch is set to current block number + // Add a piece and call nextProvingPeriod; lastProvenEpoch is unchanged until provePossession Cids.Cid[] memory pieces = new Cids.Cid[](1); pieces[0] = makeSamplePiece(2); @@ -135,8 +140,8 @@ contract PDPVerifierProofTest is MockFVMTest, ProofBuilderHelper, PieceHelper { pdpVerifier.nextProvingPeriod(setId, blockNumber + CHALLENGE_FINALITY_DELAY, empty); assertEq( pdpVerifier.getDataSetLastProvenEpoch(setId), - blockNumber, - "lastProvenEpoch should be set to block.number after first proving period piece" + creationBlock, + "lastProvenEpoch should remain at creation block until provePossession is called" ); // Schedule piece removal @@ -144,12 +149,12 @@ contract PDPVerifierProofTest is MockFVMTest, ProofBuilderHelper, PieceHelper { piecesToRemove[0] = 0; pdpVerifier.schedulePieceDeletions(setId, piecesToRemove, empty); - // Call nextProvingPeriod and verify lastProvenEpoch is reset to 0 + // Call nextProvingPeriod and verify lastProvenEpoch is set to current block after removing last piece pdpVerifier.nextProvingPeriod(setId, blockNumber + CHALLENGE_FINALITY_DELAY, empty); assertEq( pdpVerifier.getDataSetLastProvenEpoch(setId), - 0, - "lastProvenEpoch should be reset to 0 after removing last piece" + blockNumber, + "lastProvenEpoch should be set to block.number after removing last piece" ); } @@ -202,7 +207,7 @@ contract PDPVerifierProofTest is MockFVMTest, ProofBuilderHelper, PieceHelper { } function testProvePossessionFailsWithNoScheduledChallenge() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); Cids.Cid[] memory pieces = new Cids.Cid[](1); @@ -224,7 +229,7 @@ contract PDPVerifierProofTest is MockFVMTest, ProofBuilderHelper, PieceHelper { } function testEmptyProofRejected() public { - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); IPDPTypes.Proof[] memory emptyProof = new IPDPTypes.Proof[](0); @@ -409,7 +414,7 @@ contract PDPVerifierProofTest is MockFVMTest, ProofBuilderHelper, PieceHelper { } // Create new data set and add pieces. - uint256 setId = pdpVerifier.addPieces{value: PDPFees.sybilFee()}( + uint256 setId = pdpVerifier.addPieces{value: PDPFees.cleanupDeposit()}( NEW_DATA_SET_SENTINEL, address(listener), new Cids.Cid[](0), abi.encode(empty, empty) ); pdpVerifier.addPieces(setId, address(0), pieces, empty); diff --git a/tools/README.md b/tools/README.md index 68bf7f39..f4440ce4 100644 --- a/tools/README.md +++ b/tools/README.md @@ -11,13 +11,13 @@ A place for all tools related to running and developing the PDP contracts. When | Devnet | 10 epochs | ### deploy-devnet.sh -Deploys PDPVerifier to a local filecoin devnet. Assumes lotus binary is in path and local devnet is running with eth API enabled. The keystore will be funded automatically from lotus default address. Accepts optional constructor env overrides: `USDFC_TOKEN_ADDRESS`, `USDFC_SYBIL_FEE`, and `PAYMENTS_CONTRACT_ADDRESS`. By default, devnet uses zero addresses for the USDFC/payment dependencies and keeps FIL fallback enabled. +Deploys PDPVerifier to a local filecoin devnet. Assumes lotus binary is in path and local devnet is running with eth API enabled. The keystore will be funded automatically from lotus default address. ### deploy-calibnet.sh -Deploys PDPVerifier to Filecoin Calibration testnet. Accepts optional constructor env overrides: `USDFC_TOKEN_ADDRESS`, `USDFC_SYBIL_FEE`, and `PAYMENTS_CONTRACT_ADDRESS`. Defaults match the current Calibration warm-storage deployment. +Deploys PDPVerifier to Filecoin Calibration testnet. ### deploy-mainnet.sh -Deploys PDPVerifier to Filecoin mainnet. Accepts optional constructor env overrides: `USDFC_TOKEN_ADDRESS`, `USDFC_SYBIL_FEE`, and `PAYMENTS_CONTRACT_ADDRESS`. Defaults match the current Mainnet warm-storage deployment. +Deploys PDPVerifier to Filecoin mainnet. ### deploy-simple-pdp-service.sh ⚠️ DEPRECATED **As of v2.0.0, SimplePDPService is deprecated.** This optional script allows deployment of SimplePDPService for reference/community use only. Requires an existing PDPVerifier deployment. See `DEPRECATION.md` for details. diff --git a/tools/check_storage_layout.sh b/tools/check_storage_layout.sh index 8def6221..93163a10 100755 --- a/tools/check_storage_layout.sh +++ b/tools/check_storage_layout.sh @@ -85,6 +85,20 @@ compare_layouts() { local new_entry=$(jq -c --arg l "$label" '.[] | select(.label == $l)' "$new_file") if [ -z "$new_entry" ]; then + # Allow rename where only change is adding the "deprecated" prefix + local deprecated_label="deprecated${label^}" + local deprecated_entry + deprecated_entry=$(jq -c --arg l "$deprecated_label" '.[] | select(.label == $l)' "$new_file") + if [ -n "$deprecated_entry" ]; then + local dep_slot dep_offset dep_type + dep_slot=$(echo "$deprecated_entry" | jq -r '.slot') + dep_offset=$(echo "$deprecated_entry" | jq -r '.offset') + dep_type=$(echo "$deprecated_entry" | jq -cS '.type') + if [ "$dep_slot" = "$slot" ] && [ "$dep_offset" = "$offset" ] && [ "$dep_type" = "$type" ]; then + echo " Renamed: '$label' → '$deprecated_label' (slot $slot, deprecated)" + continue + fi + fi echo " DESTRUCTIVE: Variable '$label' (slot $slot, offset $offset) was removed" >&2 errors=$((errors + 1)) continue @@ -123,7 +137,29 @@ compare_layouts() { local base_match=$(jq -c --arg l "$label" '.[] | select(.label == $l)' "$base_file") if [ -z "$base_match" ]; then - if [ "$slot" -le "$max_base_slot" ]; then + # Check if this is a permitted deprecated rename (already reported in Check 1) + local is_deprecated_rename=false + if [[ "$label" == deprecated* ]]; then + local original_label="${label#deprecated}" + original_label="${original_label,}" + local base_original + base_original=$(jq -c --arg l "$original_label" '.[] | select(.label == $l)' "$base_file") + if [ -n "$base_original" ]; then + local orig_slot orig_offset orig_type entry_offset entry_type + orig_slot=$(echo "$base_original" | jq -r '.slot') + orig_offset=$(echo "$base_original" | jq -r '.offset') + orig_type=$(echo "$base_original" | jq -cS '.type') + entry_offset=$(echo "$entry" | jq -r '.offset') + entry_type=$(echo "$entry" | jq -cS '.type') + if [ "$orig_slot" = "$slot" ] && [ "$orig_offset" = "$entry_offset" ] && [ "$orig_type" = "$entry_type" ]; then + is_deprecated_rename=true + fi + fi + fi + + if $is_deprecated_rename; then + : # already reported in Check 1 + elif [ "$slot" -le "$max_base_slot" ]; then echo " DESTRUCTIVE: New variable '$label' inserted at slot $slot (must be > $max_base_slot)" >&2 errors=$((errors + 1)) else diff --git a/tools/deploy-calibnet.sh b/tools/deploy-calibnet.sh index 77d06fb3..297ccb94 100755 --- a/tools/deploy-calibnet.sh +++ b/tools/deploy-calibnet.sh @@ -20,10 +20,6 @@ fi # Calibration testnet uses 10 epochs (vs 150 on mainnet) CHALLENGE_FINALITY=10 VERIFIER_INIT_COUNTER=1 -ZERO_ADDRESS="0x0000000000000000000000000000000000000000" -USDFC_TOKEN_ADDRESS="${USDFC_TOKEN_ADDRESS:-0xb3042734b608a1B16e9e86B374A3f3e389B4cDf0}" -USDFC_SYBIL_FEE="${USDFC_SYBIL_FEE:-100000000000000000}" -PAYMENTS_CONTRACT_ADDRESS="${PAYMENTS_CONTRACT_ADDRESS:-0x09a0fDc2723fAd1A7b8e3e00eE5DF73841df55a0}" ADDR=$(cast wallet address --keystore "$KEYSTORE" --password "$PASSWORD") echo "Deploying PDP verifier from address $ADDR" @@ -31,15 +27,10 @@ echo "Deploying PDP verifier from address $ADDR" echo "PDPVerifier constructor args:" echo " initializerVersion: $VERIFIER_INIT_COUNTER" -echo " USDFC_TOKEN_ADDRESS: $USDFC_TOKEN_ADDRESS" -echo " USDFC_SYBIL_FEE: $USDFC_SYBIL_FEE" -echo " PAYMENTS_CONTRACT_ADDRESS: $PAYMENTS_CONTRACT_ADDRESS" -if [ "$USDFC_TOKEN_ADDRESS" = "$ZERO_ADDRESS" ] || [ "$PAYMENTS_CONTRACT_ADDRESS" = "$ZERO_ADDRESS" ]; then - echo " note: USDFC-backed fee path disabled; deployment will use FIL fallback only" -fi +echo " challengeFinality: $CHALLENGE_FINALITY" NONCE="$(cast nonce --rpc-url "$RPC_URL" "$ADDR")" -VERIFIER_IMPLEMENTATION_ADDRESS=$(forge create --rpc-url "$RPC_URL" --keystore "$KEYSTORE" --password "$PASSWORD" --broadcast --nonce $NONCE --chain-id 314159 src/PDPVerifier.sol:PDPVerifier --constructor-args "$VERIFIER_INIT_COUNTER" "$USDFC_TOKEN_ADDRESS" "$USDFC_SYBIL_FEE" "$PAYMENTS_CONTRACT_ADDRESS" | grep "Deployed to" | awk '{print $3}') +VERIFIER_IMPLEMENTATION_ADDRESS=$(forge create --rpc-url "$RPC_URL" --keystore "$KEYSTORE" --password "$PASSWORD" --broadcast --nonce $NONCE --chain-id 314159 src/PDPVerifier.sol:PDPVerifier --constructor-args "$VERIFIER_INIT_COUNTER" "$CHALLENGE_FINALITY" | grep "Deployed to" | awk '{print $3}') if [ -z "$VERIFIER_IMPLEMENTATION_ADDRESS" ]; then echo "Error: Failed to extract PDP verifier contract address" exit 1 @@ -48,7 +39,7 @@ echo "PDP verifier implementation deployed at: $VERIFIER_IMPLEMENTATION_ADDRESS" echo "Deploying PDP verifier proxy" NONCE=$(expr $NONCE + "1") -INIT_DATA=$(cast calldata "initialize(uint256)" $CHALLENGE_FINALITY) +INIT_DATA=$(cast calldata "initialize()") PDP_VERIFIER_ADDRESS=$(forge create --rpc-url "$RPC_URL" --keystore "$KEYSTORE" --password "$PASSWORD" --broadcast --nonce $NONCE --chain-id 314159 src/ERC1967Proxy.sol:MyERC1967Proxy --constructor-args $VERIFIER_IMPLEMENTATION_ADDRESS $INIT_DATA | grep "Deployed to" | awk '{print $3}') echo "PDP verifier deployed at: $PDP_VERIFIER_ADDRESS" diff --git a/tools/deploy-devnet.sh b/tools/deploy-devnet.sh index 131e383f..93f81734 100755 --- a/tools/deploy-devnet.sh +++ b/tools/deploy-devnet.sh @@ -26,9 +26,11 @@ sleep 5 ## Sleep for 5 seconds so fund are available and actor is registered NONCE="$(cast nonce --rpc-url "$RPC_URL" "$clientAddr")" VERIFIER_INIT_COUNTER=1 +# Devnet uses 10 epochs (same as Calibration testnet) +CHALLENGE_FINALITY=10 echo "Deploying PDP verifier" # Parse the output of forge create to extract the contract address -VERIFIER_IMPLEMENTATION_ADDRESS=$(forge create --rpc-url "$RPC_URL" --keystore "$KEYSTORE" --password "$PASSWORD" --nonce $NONCE --broadcast src/PDPVerifier.sol:PDPVerifier --constructor-args $VERIFIER_INIT_COUNTER | grep "Deployed to" | awk '{print $3}') +VERIFIER_IMPLEMENTATION_ADDRESS=$(forge create --rpc-url "$RPC_URL" --keystore "$KEYSTORE" --password "$PASSWORD" --nonce $NONCE --broadcast src/PDPVerifier.sol:PDPVerifier --constructor-args $VERIFIER_INIT_COUNTER $CHALLENGE_FINALITY | grep "Deployed to" | awk '{print $3}') if [ -z "$VERIFIER_IMPLEMENTATION_ADDRESS" ]; then echo "Error: Failed to extract PDP verifier contract address" exit 1 @@ -37,11 +39,8 @@ echo "PDP verifier implementation deployed at: $VERIFIER_IMPLEMENTATION_ADDRESS" NONCE=$(expr $NONCE + "1") -# Devnet uses 10 epochs (same as Calibration testnet) -CHALLENGE_FINALITY=10 - echo "Deploying PDP verifier proxy" -INIT_DATA=$(cast calldata "initialize(uint256)" $CHALLENGE_FINALITY) +INIT_DATA=$(cast calldata "initialize()") PDP_VERIFIER_ADDRESS=$(forge create --rpc-url "$RPC_URL" --keystore "$KEYSTORE" --password "$PASSWORD" --nonce $NONCE --broadcast src/ERC1967Proxy.sol:MyERC1967Proxy --constructor-args $VERIFIER_IMPLEMENTATION_ADDRESS $INIT_DATA | grep "Deployed to" | awk '{print $3}') echo "PDP verifier deployed at: $PDP_VERIFIER_ADDRESS" diff --git a/tools/deploy-mainnet.sh b/tools/deploy-mainnet.sh index 7905ff94..3b89d9eb 100755 --- a/tools/deploy-mainnet.sh +++ b/tools/deploy-mainnet.sh @@ -20,10 +20,6 @@ fi # Mainnet uses 150 epochs (vs 10 on Calibration testnet) CHALLENGE_FINALITY=150 VERIFIER_INIT_COUNTER=1 -ZERO_ADDRESS="0x0000000000000000000000000000000000000000" -USDFC_TOKEN_ADDRESS="${USDFC_TOKEN_ADDRESS:-0x80B98d3aa09ffff255c3ba4A241111Ff1262F045}" -USDFC_SYBIL_FEE="${USDFC_SYBIL_FEE:-100000000000000000}" -PAYMENTS_CONTRACT_ADDRESS="${PAYMENTS_CONTRACT_ADDRESS:-0x23b1e018F08BB982348b15a86ee926eEBf7F4DAa}" ADDR=$(cast wallet address --keystore "$KEYSTORE" --password "$PASSWORD") echo "Deploying PDP verifier from address $ADDR" @@ -31,15 +27,10 @@ echo "Deploying PDP verifier from address $ADDR" echo "PDPVerifier constructor args:" echo " initializerVersion: $VERIFIER_INIT_COUNTER" -echo " USDFC_TOKEN_ADDRESS: $USDFC_TOKEN_ADDRESS" -echo " USDFC_SYBIL_FEE: $USDFC_SYBIL_FEE" -echo " PAYMENTS_CONTRACT_ADDRESS: $PAYMENTS_CONTRACT_ADDRESS" -if [ "$USDFC_TOKEN_ADDRESS" = "$ZERO_ADDRESS" ] || [ "$PAYMENTS_CONTRACT_ADDRESS" = "$ZERO_ADDRESS" ]; then - echo " note: USDFC-backed fee path disabled; deployment will use FIL fallback only" -fi +echo " challengeFinality: $CHALLENGE_FINALITY" NONCE="$(cast nonce --rpc-url "$RPC_URL" "$ADDR")" -VERIFIER_IMPLEMENTATION_ADDRESS=$(forge create --rpc-url "$RPC_URL" --keystore "$KEYSTORE" --password "$PASSWORD" --broadcast --nonce $NONCE --chain-id 314 src/PDPVerifier.sol:PDPVerifier --constructor-args "$VERIFIER_INIT_COUNTER" "$USDFC_TOKEN_ADDRESS" "$USDFC_SYBIL_FEE" "$PAYMENTS_CONTRACT_ADDRESS" | grep "Deployed to" | awk '{print $3}') +VERIFIER_IMPLEMENTATION_ADDRESS=$(forge create --rpc-url "$RPC_URL" --keystore "$KEYSTORE" --password "$PASSWORD" --broadcast --nonce $NONCE --chain-id 314 src/PDPVerifier.sol:PDPVerifier --constructor-args "$VERIFIER_INIT_COUNTER" "$CHALLENGE_FINALITY" | grep "Deployed to" | awk '{print $3}') if [ -z "$VERIFIER_IMPLEMENTATION_ADDRESS" ]; then echo "Error: Failed to extract PDP verifier contract address" exit 1 @@ -48,7 +39,7 @@ echo "PDP verifier implementation deployed at: $VERIFIER_IMPLEMENTATION_ADDRESS" echo "Deploying PDP verifier proxy" NONCE=$(expr $NONCE + "1") -INIT_DATA=$(cast calldata "initialize(uint256)" $CHALLENGE_FINALITY) +INIT_DATA=$(cast calldata "initialize()") PDP_VERIFIER_ADDRESS=$(forge create --rpc-url "$RPC_URL" --keystore "$KEYSTORE" --password "$PASSWORD" --broadcast --nonce $NONCE --chain-id 314 src/ERC1967Proxy.sol:MyERC1967Proxy --constructor-args $VERIFIER_IMPLEMENTATION_ADDRESS $INIT_DATA | grep "Deployed to" | awk '{print $3}') echo "PDP verifier deployed at: $PDP_VERIFIER_ADDRESS" diff --git a/tools/deploy-transfer-ownership-upgrade-calibnet.sh b/tools/deploy-transfer-ownership-upgrade-calibnet.sh index 997f7ceb..d605999d 100755 --- a/tools/deploy-transfer-ownership-upgrade-calibnet.sh +++ b/tools/deploy-transfer-ownership-upgrade-calibnet.sh @@ -16,8 +16,9 @@ COMPILER_VERSION="${COMPILER_VERSION:-0.8.22}" ##################################### # 1. Create INIT_DATA # ##################################### -echo "Generating calldata for initialize(uint256) with argument 150 ..." -INIT_DATA=$(cast calldata "initialize(uint256)" 150) +CHALLENGE_FINALITY=150 +echo "Generating calldata for initialize() ..." +INIT_DATA=$(cast calldata "initialize()") echo "INIT_DATA = $INIT_DATA" echo @@ -44,7 +45,7 @@ DEPLOY_OUTPUT_VERIFIER=$( --broadcast \ --nonce $NONCE \ src/PDPVerifier.sol:PDPVerifier \ - --constructor-args $VERIFIER_INIT_COUNTER + --constructor-args $VERIFIER_INIT_COUNTER $CHALLENGE_FINALITY ) NONCE=$(expr $NONCE + "1") @@ -121,7 +122,8 @@ echo "========================================" echo "Deploying a new PDPVerifier contract ..." # For the upgrade, compute next initializer counter from the proxy and pass it UPGRADE_INIT_COUNTER=$(expr "$("$SCRIPT_DIR/get-initialized-counter.sh" "$PROXY_ADDRESS")" + 1) -DEPLOY_OUTPUT_VERIFIER_2=$(forge create --nonce $NONCE --broadcast --rpc-url "$FIL_CALIBNET_RPC_URL" --private-key "$FIL_CALIBNET_PRIVATE_KEY" --chain-id "$CHAIN_ID" src/PDPVerifier.sol:PDPVerifier --constructor-args $UPGRADE_INIT_COUNTER) +UPGRADE_CHALLENGE_FINALITY=$(cast call --rpc-url "$FIL_CALIBNET_RPC_URL" "$PROXY_ADDRESS" "getChallengeFinality()(uint256)") +DEPLOY_OUTPUT_VERIFIER_2=$(forge create --nonce $NONCE --broadcast --rpc-url "$FIL_CALIBNET_RPC_URL" --private-key "$FIL_CALIBNET_PRIVATE_KEY" --chain-id "$CHAIN_ID" src/PDPVerifier.sol:PDPVerifier --constructor-args $UPGRADE_INIT_COUNTER $UPGRADE_CHALLENGE_FINALITY) NONCE=$(expr $NONCE + "1") PDP_VERIFIER_ADDRESS_2=$(echo "$DEPLOY_OUTPUT_VERIFIER_2" | grep "Deployed to" | awk '{print $3}') echo "PDPVerifier deployed at: $PDP_VERIFIER_ADDRESS_2" diff --git a/tools/upgrade-contract.sh b/tools/upgrade-contract.sh index be659779..7c73ef91 100755 --- a/tools/upgrade-contract.sh +++ b/tools/upgrade-contract.sh @@ -9,7 +9,6 @@ # Set DRY_RUN=false to actually deploy and broadcast transactions (default is dry-run for safety) DRY_RUN=${DRY_RUN:-true} SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" -ZERO_ADDRESS="0x0000000000000000000000000000000000000000" PDP_VERIFIER_IMPLEMENTATION_PATH="src/PDPVerifier.sol:PDPVerifier" if [ "$DRY_RUN" = "true" ]; then @@ -54,43 +53,18 @@ if [ -z "$IMPLEMENTATION_PATH" ]; then fi UPGRADE_INIT_COUNTER=$(expr "$("$SCRIPT_DIR/get-initialized-counter.sh" "$PROXY_ADDRESS")" + 1) -CONSTRUCTOR_ARGS=("$UPGRADE_INIT_COUNTER") +CHALLENGE_FINALITY=$(cast call --rpc-url "$RPC_URL" "$PROXY_ADDRESS" "getChallengeFinality()(uint256)") if [ "$IMPLEMENTATION_PATH" != "$PDP_VERIFIER_IMPLEMENTATION_PATH" ]; then echo "Error: Only PDPVerifier upgrades are supported. Got: $IMPLEMENTATION_PATH" exit 1 fi -case "$CHAIN_ID" in - "314") - USDFC_TOKEN_ADDRESS="${USDFC_TOKEN_ADDRESS:-0x80B98d3aa09ffff255c3ba4A241111Ff1262F045}" - PAYMENTS_CONTRACT_ADDRESS="${PAYMENTS_CONTRACT_ADDRESS:-0x23b1e018F08BB982348b15a86ee926eEBf7F4DAa}" - ;; - "314159") - USDFC_TOKEN_ADDRESS="${USDFC_TOKEN_ADDRESS:-0xb3042734b608a1B16e9e86B374A3f3e389B4cDf0}" - PAYMENTS_CONTRACT_ADDRESS="${PAYMENTS_CONTRACT_ADDRESS:-0x09a0fDc2723fAd1A7b8e3e00eE5DF73841df55a0}" - ;; - "31415926") - USDFC_TOKEN_ADDRESS="${USDFC_TOKEN_ADDRESS:-$ZERO_ADDRESS}" - PAYMENTS_CONTRACT_ADDRESS="${PAYMENTS_CONTRACT_ADDRESS:-$ZERO_ADDRESS}" - ;; - *) - echo "Error: Unsupported chain ID for default PDPVerifier constructor args" - echo "Please set USDFC_TOKEN_ADDRESS and PAYMENTS_CONTRACT_ADDRESS explicitly." - exit 1 - ;; -esac -USDFC_SYBIL_FEE="${USDFC_SYBIL_FEE:-100000000000000000}" -CONSTRUCTOR_ARGS=("$UPGRADE_INIT_COUNTER" "$USDFC_TOKEN_ADDRESS" "$USDFC_SYBIL_FEE" "$PAYMENTS_CONTRACT_ADDRESS") +CONSTRUCTOR_ARGS=("$UPGRADE_INIT_COUNTER" "$CHALLENGE_FINALITY") echo "Using PDPVerifier constructor args:" echo " initializerVersion: $UPGRADE_INIT_COUNTER" -echo " USDFC_TOKEN_ADDRESS: $USDFC_TOKEN_ADDRESS" -echo " USDFC_SYBIL_FEE: $USDFC_SYBIL_FEE" -echo " PAYMENTS_CONTRACT_ADDRESS: $PAYMENTS_CONTRACT_ADDRESS" -if [ "$USDFC_TOKEN_ADDRESS" = "$ZERO_ADDRESS" ] || [ "$PAYMENTS_CONTRACT_ADDRESS" = "$ZERO_ADDRESS" ]; then - echo " note: USDFC-backed fee path disabled; deployment will use FIL fallback only" -fi +echo " challengeFinality: $CHALLENGE_FINALITY" if [ "$DRY_RUN" = "true" ]; then echo "🔍 Simulating deployment of new $IMPLEMENTATION_PATH implementation contract"