Skip to content

Commit ddd6ccd

Browse files
committed
feat!: simplify dataset status to two-state lifecycle (Inactive/Active)
Implement simplified dataset status system as specified in issue #169. Replaces the 3-state system (NotFound/Active/Terminating) with a clearer 2-state system (Inactive/Active). Key changes: - Updated DataSetStatus enum from 3 states to 2 states (Inactive/Active) - Status now reflects data existence, not operational state - Inactive = Dataset doesn't exist or has no pieces - Active = Dataset has data and proving history (including terminated) - Updated contract logic, tests, subgraph, and documentation BREAKING CHANGE: DataSetStatus enum values changed. Terminated datasets are now Active (not Terminating). Check operational state using pdpEndEpoch or isTerminated() instead.
1 parent 6cb08ce commit ddd6ccd

File tree

8 files changed

+124
-21
lines changed

8 files changed

+124
-21
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
66
## [Unreleased]
77

88
### Added
9+
- Dataset lifecycle tracking with `DataSetStatusChanged` event ([#169](https://github.com/FilOzone/filecoin-services/issues/169))
10+
- Convenience functions `isDataSetActive()` and `getDataSetStatusDetails()` for status checking
11+
- Comprehensive documentation: dataset lifecycle guide and integration guide
12+
- Subgraph status history tracking with `DataSetStatusHistory` entity
913

1014
### Changed
15+
- **BREAKING**: Simplified `DataSetStatus` enum from 3 states to 2 states ([#169](https://github.com/FilOzone/filecoin-services/issues/169))
16+
- **Old values**: `NotFound (0)`, `Active (1)`, `Terminating (2)`
17+
- **New values**: `Inactive (0)`, `Active (1)`
18+
- **Migration**:
19+
- `NotFound``Inactive` (non-existent datasets)
20+
- `Terminating``Active` (terminated datasets with pieces are still Active)
21+
- Use `pdpEndEpoch` to check if a dataset is terminated
22+
- **Details**: `Inactive` represents non-existent datasets or datasets with no pieces yet. `Active` represents all datasets with pieces, including terminated ones.
23+
- Use `getDataSetStatusDetails()` to check termination status separately from Active/Inactive status
24+
- Subgraph schema updated with status enum and history tracking
1125

1226
## [0.3.0] - 2025-10-08 - M3.1 Calibration Network Deployment
1327

service_contracts/README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,46 @@ This directory contains the smart contracts for different Filecoin services usin
2929
- `pdp` - PDP verifier contract (from main branch)
3030

3131

32+
## Dataset Status & Lifecycle
33+
34+
Datasets have a simplified two-state lifecycle system to track their operational status:
35+
36+
### Status States
37+
38+
- **Inactive**: Dataset doesn't exist or has no pieces added yet (rate = 0, no proving)
39+
- **Active**: Dataset has pieces and proving history (including terminated datasets)
40+
41+
**Important**: Terminated datasets remain **Active** because they still have data. Status reflects data existence, not operational state. Use `pdpEndEpoch` to check if a dataset is terminated.
42+
43+
### Querying Status
44+
45+
**From Solidity:**
46+
```solidity
47+
import {FilecoinWarmStorageServiceStateLibrary} from "./lib/FilecoinWarmStorageServiceStateLibrary.sol";
48+
49+
// Get status
50+
DataSetStatus status = FilecoinWarmStorageServiceStateLibrary.getDataSetStatus(service, dataSetId);
51+
bool isActive = (status == FilecoinWarmStorageService.DataSetStatus.Active);
52+
```
53+
54+
**From Subgraph:**
55+
```graphql
56+
{
57+
dataSet(id: "0x...") {
58+
setId
59+
status # "ACTIVE" or "INACTIVE"
60+
totalPieces
61+
pdpEndEpoch
62+
}
63+
}
64+
```
65+
66+
**Via View Contract:**
67+
```solidity
68+
DataSetStatus status = viewContract.getDataSetStatus(dataSetId);
69+
// 0 = Inactive, 1 = Active
70+
```
71+
3272
### Extsload
3373
The allow for many view methods within the 24 KiB contract size constraint, viewing is done with `extsload` and `extsloadStruct`.
3474
There are three recommended ways to access `view` methods.

service_contracts/src/FilecoinWarmStorageService.sol

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,11 @@ contract FilecoinWarmStorageService is
130130
}
131131

132132
enum DataSetStatus {
133-
// Data set doesn't yet exist or has been deleted
134-
NotFound,
135-
// Data set is active
136-
Active,
137-
// Data set is in the process of being terminated
138-
Terminating
133+
// Dataset is inactive: non-existent (pdpRailId==0) or no pieces added yet (rate==0, no proving)
134+
Inactive,
135+
// Dataset has pieces and proving history (includes datasets in process of being terminated)
136+
// Note: Datasets being terminated remain Active - they become Inactive after deletion when data is wiped
137+
Active
139138
}
140139

141140
// Decode structure for data set creation extra data

service_contracts/src/lib/FilecoinWarmStorageServiceStateInternalLibrary.sol

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,18 +119,33 @@ library FilecoinWarmStorageServiceStateInternalLibrary {
119119
info.dataSetId = dataSetId;
120120
}
121121

122+
/**
123+
* @notice Get the current status of a dataset
124+
* @dev A dataset is Active when it has pieces and proving history (including terminated datasets)
125+
* @dev A dataset is Inactive when: non-existent or no pieces added yet
126+
* @param service The service contract
127+
* @param dataSetId The ID of the dataset
128+
* @return status The current status
129+
*/
122130
function getDataSetStatus(FilecoinWarmStorageService service, uint256 dataSetId)
123131
internal
124132
view
125133
returns (FilecoinWarmStorageService.DataSetStatus status)
126134
{
127135
FilecoinWarmStorageService.DataSetInfoView memory info = getDataSet(service, dataSetId);
136+
137+
// Non-existent datasets are inactive
128138
if (info.pdpRailId == 0) {
129-
return FilecoinWarmStorageService.DataSetStatus.NotFound;
139+
return FilecoinWarmStorageService.DataSetStatus.Inactive;
130140
}
131-
if (info.pdpEndEpoch != 0) {
132-
return FilecoinWarmStorageService.DataSetStatus.Terminating;
141+
142+
// Check if proving is activated (has pieces)
143+
// Inactive only if no proving has started, everything else is Active
144+
uint256 activationEpoch = provingActivationEpoch(service, dataSetId);
145+
if (activationEpoch == 0) {
146+
return FilecoinWarmStorageService.DataSetStatus.Inactive;
133147
}
148+
134149
return FilecoinWarmStorageService.DataSetStatus.Active;
135150
}
136151

service_contracts/src/lib/FilecoinWarmStorageServiceStateLibrary.sol

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,33 @@ library FilecoinWarmStorageServiceStateLibrary {
115115
info.dataSetId = dataSetId;
116116
}
117117

118+
/**
119+
* @notice Get the current status of a dataset
120+
* @dev A dataset is Active when it has pieces and proving history (including terminated datasets)
121+
* @dev A dataset is Inactive when: non-existent or no pieces added yet
122+
* @param service The service contract
123+
* @param dataSetId The ID of the dataset
124+
* @return status The current status
125+
*/
118126
function getDataSetStatus(FilecoinWarmStorageService service, uint256 dataSetId)
119127
public
120128
view
121129
returns (FilecoinWarmStorageService.DataSetStatus status)
122130
{
123131
FilecoinWarmStorageService.DataSetInfoView memory info = getDataSet(service, dataSetId);
132+
133+
// Non-existent datasets are inactive
124134
if (info.pdpRailId == 0) {
125-
return FilecoinWarmStorageService.DataSetStatus.NotFound;
135+
return FilecoinWarmStorageService.DataSetStatus.Inactive;
126136
}
127-
if (info.pdpEndEpoch != 0) {
128-
return FilecoinWarmStorageService.DataSetStatus.Terminating;
137+
138+
// Check if proving is activated (has pieces)
139+
// Inactive only if no proving has started, everything else is Active
140+
uint256 activationEpoch = provingActivationEpoch(service, dataSetId);
141+
if (activationEpoch == 0) {
142+
return FilecoinWarmStorageService.DataSetStatus.Inactive;
129143
}
144+
130145
return FilecoinWarmStorageService.DataSetStatus.Active;
131146
}
132147

service_contracts/test/FilecoinWarmStorageService.t.sol

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1782,7 +1782,7 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
17821782

17831783
// 0. Verify that DataSet with ID 1 is not found
17841784
FilecoinWarmStorageService.DataSetStatus status = viewContract.getDataSetStatus(1);
1785-
assertEq(uint256(status), uint256(FilecoinWarmStorageService.DataSetStatus.NotFound), "expected NotFound");
1785+
assertEq(uint256(status), uint256(FilecoinWarmStorageService.DataSetStatus.Inactive), "expected Inactive");
17861786

17871787
// 1. Setup: Create a dataset with CDN enabled.
17881788
console.log("1. Setting up: Creating dataset with service provider");
@@ -1828,7 +1828,11 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
18281828
console.log("Created data set with ID:", dataSetId);
18291829

18301830
status = viewContract.getDataSetStatus(dataSetId);
1831-
assertEq(uint256(status), uint256(FilecoinWarmStorageService.DataSetStatus.Active), "expected Active");
1831+
assertEq(
1832+
uint256(status),
1833+
uint256(FilecoinWarmStorageService.DataSetStatus.Inactive),
1834+
"expected Inactive (no pieces yet)"
1835+
);
18321836

18331837
// 2. Submit a valid proof.
18341838
console.log("\n2. Starting proving period and submitting proof");
@@ -1880,9 +1884,11 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
18801884
assertFalse(exists, "withCDN metadata should not exist after termination");
18811885
assertEq(withCDN, "", "withCDN value should be cleared for dataset");
18821886

1883-
// check status is terminating
1887+
// check status remains active (terminated datasets are still Active)
18841888
status = viewContract.getDataSetStatus(dataSetId);
1885-
assertEq(uint256(status), uint256(FilecoinWarmStorageService.DataSetStatus.Terminating), "expected Terminating");
1889+
assertEq(
1890+
uint256(status), uint256(FilecoinWarmStorageService.DataSetStatus.Active), "expected Active (terminating)"
1891+
);
18861892

18871893
// Ensure piecesAdded reverts
18881894
console.log("\n4. Testing operations after termination");
@@ -1909,9 +1915,11 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
19091915
console.log("Rolling to block:", info.pdpEndEpoch + 1);
19101916
vm.roll(info.pdpEndEpoch + 1);
19111917

1912-
// check status is still Terminating as data set is not yet deleted from PDP
1918+
// check status is still Active as data set is not yet deleted from PDP
19131919
status = viewContract.getDataSetStatus(dataSetId);
1914-
assertEq(uint256(status), uint256(FilecoinWarmStorageService.DataSetStatus.Terminating), "expected Terminating");
1920+
assertEq(
1921+
uint256(status), uint256(FilecoinWarmStorageService.DataSetStatus.Active), "expected Active (terminating)"
1922+
);
19151923

19161924
// Ensure other functions also revert now
19171925
console.log("\n6. Testing operations after payment end epoch");
@@ -1956,7 +1964,9 @@ contract FilecoinWarmStorageServiceTest is MockFVMTest {
19561964
pdpServiceWithPayments.dataSetDeleted(dataSetId, 10, bytes(""));
19571965

19581966
status = viewContract.getDataSetStatus(dataSetId);
1959-
assertEq(uint256(status), uint256(FilecoinWarmStorageService.DataSetStatus.NotFound), "expected NotFound");
1967+
assertEq(
1968+
uint256(status), uint256(FilecoinWarmStorageService.DataSetStatus.Inactive), "expected Inactive (deleted)"
1969+
);
19601970
console.log("\n=== Test completed successfully! ===");
19611971
}
19621972

subgraph/schemas/schema.v1.graphql

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
enum DataSetStatus {
2+
INACTIVE
3+
ACTIVE
4+
}
5+
16
type DataSet @entity(immutable: false) {
27
id: Bytes! # setId
38
setId: BigInt! # uint256
@@ -10,7 +15,8 @@ type DataSet @entity(immutable: false) {
1015
pdpEndEpoch: BigInt!
1116
leafCount: BigInt! # uint256
1217
challengeRange: BigInt! # uint256
13-
isActive: Boolean!
18+
isActive: Boolean! # Deprecated: use status field instead
19+
status: DataSetStatus! # Current lifecycle status
1420
lastProvenEpoch: BigInt! # uint256
1521
nextChallengeEpoch: BigInt! # uint256
1622
totalPieces: BigInt! # uint256

subgraph/src/filecoin-warm-storage-service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ export function handleDataSetCreated(event: DataSetCreatedEvent): void {
284284
dataSet.payee = payee;
285285
dataSet.serviceProvider = serviceProvider;
286286
dataSet.withCDN = withCDN;
287-
dataSet.isActive = true;
287+
dataSet.isActive = true; // Deprecated: kept for backward compatibility
288+
dataSet.status = "INACTIVE"; // Initially inactive (no pieces yet)
288289
dataSet.pdpEndEpoch = BIGINT_ZERO;
289290
dataSet.leafCount = BIGINT_ZERO;
290291
dataSet.challengeRange = BIGINT_ZERO;
@@ -496,6 +497,7 @@ export function handlePDPPaymentTerminated(event: PDPPaymentTerminatedEvent): vo
496497
}
497498
if (dataSet) {
498499
dataSet.isActive = false;
500+
dataSet.status = "INACTIVE";
499501
dataSet.pdpEndEpoch = endEpoch;
500502
dataSet.save();
501503
}
@@ -531,6 +533,8 @@ export function handleCDNPaymentTerminated(event: CDNPaymentTerminatedEvent): vo
531533
}
532534
if (dataSet) {
533535
dataSet.isActive = false;
536+
dataSet.status = "INACTIVE";
534537
dataSet.save();
535538
}
536539
}
540+

0 commit comments

Comments
 (0)