Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,389 changes: 2,389 additions & 0 deletions subgraph/abis/FilecoinWarmStorageService.json

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions subgraph/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ type DataSet @entity(immutable: false) {
owner: Provider! # address of the provider
leafCount: BigInt! # uint256
challengeRange: BigInt! # uint256
# True iff the dataset has not been deleted (PDPVerifier.DataSetDeleted)
# and the FWSS service has not been terminated (FWSS.ServiceTerminated).
# Note: PDPPaymentTerminated does NOT affect this flag; use pdpPaymentEndEpoch.
isActive: Boolean!
status: DataSetStatus!
lastProvenEpoch: BigInt! # uint256
Expand All @@ -38,6 +41,27 @@ type DataSet @entity(immutable: false) {
updatedAt: BigInt!
blockNumber: BigInt!

# ---- FWSS fields (null / empty for non-FWSS datasets) ----
# Populated by FWSS.DataSetCreated handler.
fwssProviderId: BigInt # uint256 — FWSS numeric provider ID
fwssPayer: Bytes # address of the payer
fwssServiceProvider: Bytes # address — may diverge from owner after transfers
fwssPdpRailId: BigInt # uint256 — FWSS PDP rail ID

# Raw metadata from DataSetCreated (kept for completeness; clients should
# prefer the derived booleans below for filter queries).
metadataKeys: [String!]! # empty array when not FWSS
metadataValues: [String!]! # empty array when not FWSS

# Derived metadata flags (cheap to filter on).
withIPFSIndexing: Boolean! # true iff "withIPFSIndexing" in metadataKeys
withCDN: Boolean! # true iff "withCDN" in metadataKeys

# Populated by FWSS.PDPPaymentTerminated handler.
# May be in the past (already terminated) or future (terminating).
# Does NOT flip isActive — clients that care must compare to current epoch.
pdpPaymentEndEpoch: BigInt

# Derived relationships
roots: [Root!]! @derivedFrom(field: "proofSet")
transactions: [Transaction!]! @derivedFrom(field: "proofSet")
Expand Down Expand Up @@ -113,6 +137,12 @@ type Root @entity(immutable: false) {
updatedAt: BigInt!
blockNumber: BigInt!

# ---- FWSS fields (null / empty for non-FWSS pieces) ----
# Populated by FWSS.PieceAdded handler.
metadataKeys: [String!]! # empty array when not FWSS
metadataValues: [String!]! # empty array when not FWSS
ipfsRootCID: String # values[indexOf(keys, "ipfsRootCID")] or null

# Relationship
proofSet: DataSet! # Link to DataSet (stores DataSet ID)
# Derived relationships
Expand Down
182 changes: 182 additions & 0 deletions subgraph/src/fwss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { BigInt, Bytes, log } from "@graphprotocol/graph-ts";
import {
DataSetCreated as DataSetCreatedEvent,
PieceAdded as PieceAddedEvent,
ServiceTerminated as ServiceTerminatedEvent,
PDPPaymentTerminated as PDPPaymentTerminatedEvent,
DataSetServiceProviderChanged as DataSetServiceProviderChangedEvent,
} from "../generated/FilecoinWarmStorageService/FilecoinWarmStorageService";
import { DataSet, Root } from "../generated/schema";
import { getRootEntityId } from "./pdp-verifier";
import { saveNetworkMetrics } from "./helper";

// ---- Helpers --------------------------------------------------------------

function getProofSetEntityId(setId: BigInt): Bytes {
return Bytes.fromByteArray(Bytes.fromBigInt(setId));
}

function arrayContains(arr: string[], needle: string): boolean {
for (let i = 0; i < arr.length; i++) {
if (arr[i] == needle) return true;
}
return false;
}

function extractMetadataValue(
keys: string[],
values: string[],
needle: string
): string | null {
for (let i = 0; i < keys.length; i++) {
if (keys[i] == needle) {
return i < values.length ? values[i] : null;
}
}
return null;
}

// ---- Handlers -------------------------------------------------------------

export function handleFwssDataSetCreated(event: DataSetCreatedEvent): void {
const id = getProofSetEntityId(event.params.dataSetId);
// FWSS.DataSetCreated fires BEFORE PDPVerifier's own DataSetCreated event
// (see PDPVerifier._createDataSet: listener callback runs first, THEN
// `emit DataSetCreated`). If no entity exists yet, create a stub with
// required defaults; pdp-verifier.handleDataSetCreated will run later in
// the same block and fill in PDPVerifier-level fields. Since handlers run
// sequentially and atomically within a block, no GraphQL query can observe
// that intermediate state.
let ds = DataSet.load(id);
if (ds == null) {
ds = new DataSet(id);
ds.setId = event.params.dataSetId;
// PDPVerifier-level non-null fields — safe defaults; handleDataSetCreated
// will overwrite shortly after in this same block.
ds.owner = event.params.serviceProvider;
ds.listener = event.address;
ds.isActive = true;
ds.leafCount = BigInt.fromI32(0);
ds.challengeRange = BigInt.fromI32(0);
ds.lastProvenEpoch = BigInt.fromI32(0);
ds.nextChallengeEpoch = BigInt.fromI32(0);
ds.firstDeadline = BigInt.fromI32(0);
ds.maxProvingPeriod = BigInt.fromI32(0);
ds.challengeWindowSize = BigInt.fromI32(0);
ds.currentDeadlineCount = BigInt.fromI32(0);
ds.nextDeadline = BigInt.fromI32(0);
ds.provenThisPeriod = false;
ds.totalRoots = BigInt.fromI32(0);
ds.nextPieceId = BigInt.fromI32(0);
ds.totalDataSize = BigInt.fromI32(0);
ds.totalFeePaid = BigInt.fromI32(0);
ds.totalFaultedPeriods = BigInt.fromI32(0);
ds.totalFaultedRoots = BigInt.fromI32(0);
ds.totalProofs = BigInt.fromI32(0);
ds.totalProvedRoots = BigInt.fromI32(0);
ds.totalTransactions = BigInt.fromI32(0);
ds.totalEventLogs = BigInt.fromI32(0);
ds.createdAt = event.block.timestamp;
ds.blockNumber = event.block.number;
// status: EMPTY. Imported enum value would be cleaner, but schema.graphql
// defines the enum; matching literal is what the generated code stores.
ds.status = "EMPTY";
}

ds.fwssProviderId = event.params.providerId;
ds.fwssPayer = event.params.payer;
ds.fwssServiceProvider = event.params.serviceProvider;
ds.fwssPdpRailId = event.params.pdpRailId;
ds.metadataKeys = event.params.metadataKeys;
ds.metadataValues = event.params.metadataValues;
ds.withIPFSIndexing = arrayContains(
event.params.metadataKeys,
"withIPFSIndexing"
);
ds.withCDN = arrayContains(event.params.metadataKeys, "withCDN");
ds.updatedAt = event.block.timestamp;
ds.save();
}

export function handleFwssPieceAdded(event: PieceAddedEvent): void {
const id = getRootEntityId(event.params.dataSetId, event.params.pieceId);
const root = Root.load(id);
if (root == null) {
log.warning("FWSS PieceAdded for unknown root {}-{}", [
event.params.dataSetId.toString(),
event.params.pieceId.toString(),
]);
return;
}

root.metadataKeys = event.params.keys;
root.metadataValues = event.params.values;
root.ipfsRootCID = extractMetadataValue(
event.params.keys,
event.params.values,
"ipfsRootCID"
);
root.updatedAt = event.block.timestamp;
root.save();
}

export function handleFwssServiceTerminated(
event: ServiceTerminatedEvent
): void {
const id = getProofSetEntityId(event.params.dataSetId);
const ds = DataSet.load(id);
if (ds == null) {
log.warning("FWSS ServiceTerminated for unknown dataSet {}", [
event.params.dataSetId.toString(),
]);
return;
}

// Guard against double-decrement of totalActiveProofSets in case both
// DataSetDeleted (PDPVerifier) and ServiceTerminated (FWSS) fire for the
// same dataset. Only decrement if this event is the one flipping isActive.
if (ds.isActive) {
saveNetworkMetrics(
["totalActiveProofSets"],
[BigInt.fromI32(1)],
["subtract"]
);
}
ds.isActive = false;
ds.updatedAt = event.block.timestamp;
ds.save();
}

export function handleFwssPdpPaymentTerminated(
event: PDPPaymentTerminatedEvent
): void {
const id = getProofSetEntityId(event.params.dataSetId);
const ds = DataSet.load(id);
if (ds == null) {
log.warning("FWSS PDPPaymentTerminated for unknown dataSet {}", [
event.params.dataSetId.toString(),
]);
return;
}

ds.pdpPaymentEndEpoch = event.params.endEpoch;
ds.updatedAt = event.block.timestamp;
ds.save();
}

export function handleFwssDataSetServiceProviderChanged(
event: DataSetServiceProviderChangedEvent
): void {
const id = getProofSetEntityId(event.params.dataSetId);
const ds = DataSet.load(id);
if (ds == null) {
log.warning("FWSS DataSetServiceProviderChanged for unknown dataSet {}", [
event.params.dataSetId.toString(),
]);
return;
}

ds.fwssServiceProvider = event.params.newServiceProvider;
ds.updatedAt = event.block.timestamp;
ds.save();
}
22 changes: 20 additions & 2 deletions subgraph/src/pdp-verifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,22 @@ export function handleDataSetCreated(event: DataSetCreatedEvent): void {
transaction.save();
}

// Create DataSet
let proofSet = new DataSet(proofSetEntityId);
// Create or load DataSet. FWSS.dataSetCreated callback fires before
// PDPVerifier's own DataSetCreated event (see PDPVerifier._createDataSet:
// listener callback runs, THEN `emit DataSetCreated`). If the listener is
// FWSS, handleFwssDataSetCreated has already created a stub entity with
// FWSS-layer fields populated. Load to preserve those fields.
let proofSet = DataSet.load(proofSetEntityId);
if (proofSet == null) {
proofSet = new DataSet(proofSetEntityId);
// FWSS fields — defaulted on create; FWSS handler will overwrite if it fires later.
proofSet.metadataKeys = [];
proofSet.metadataValues = [];
proofSet.withIPFSIndexing = false;
proofSet.withCDN = false;
// fwssProviderId, fwssPayer, fwssServiceProvider, fwssPdpRailId,
// pdpPaymentEndEpoch are nullable — no init needed.
}
proofSet.setId = event.params.setId;
proofSet.owner = providerEntityId; // Link to Provider via owner address (which is Provider's ID)
proofSet.listener = listenerAddr;
Expand Down Expand Up @@ -1189,6 +1203,10 @@ export function handlePiecesAdded(event: PiecesAddedEvent): void {
root.updatedAt = event.block.timestamp;
root.blockNumber = event.block.number;
root.proofSet = proofSetEntityId; // Link to DataSet
// FWSS fields — defaulted here; patched in FWSS handler if applicable.
root.metadataKeys = [];
root.metadataValues = [];
// ipfsRootCID is nullable — no init needed.

root.save();

Expand Down
29 changes: 29 additions & 0 deletions subgraph/subgraph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,32 @@ dataSources:
- event: NextProvingPeriod(indexed uint256,uint256,uint256)
handler: handleNextProvingPeriod
file: ./src/pdp-verifier.ts
- kind: ethereum
name: FilecoinWarmStorageService
network: filecoin
source:
address: "0x8408502033C418E1bbC97cE9ac48E5528F371A9f"
abi: FilecoinWarmStorageService
startBlock: 5459617
mapping:
kind: ethereum/events
apiVersion: 0.0.9
language: wasm/assemblyscript
entities:
- DataSet
- Root
abis:
- name: FilecoinWarmStorageService
file: ./abis/FilecoinWarmStorageService.json
eventHandlers:
- event: DataSetCreated(indexed uint256,indexed uint256,uint256,uint256,uint256,address,address,address,string[],string[])
handler: handleFwssDataSetCreated
- event: PieceAdded(indexed uint256,indexed uint256,(bytes),string[],string[])
handler: handleFwssPieceAdded
- event: ServiceTerminated(indexed address,indexed uint256,uint256,uint256,uint256)
handler: handleFwssServiceTerminated
- event: PDPPaymentTerminated(indexed uint256,uint256,uint256)
handler: handleFwssPdpPaymentTerminated
- event: DataSetServiceProviderChanged(indexed uint256,indexed address,indexed address)
handler: handleFwssDataSetServiceProviderChanged
file: ./src/fwss.ts
29 changes: 29 additions & 0 deletions subgraph/subgraph_mainnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,32 @@ dataSources:
- event: NextProvingPeriod(indexed uint256,uint256,uint256)
handler: handleNextProvingPeriod
file: ./src/pdp-verifier.ts
- kind: ethereum
name: FilecoinWarmStorageService
network: filecoin
source:
address: "0x8408502033C418E1bbC97cE9ac48E5528F371A9f"
abi: FilecoinWarmStorageService
startBlock: 5459617
mapping:
kind: ethereum/events
apiVersion: 0.0.9
language: wasm/assemblyscript
entities:
- DataSet
- Root
abis:
- name: FilecoinWarmStorageService
file: ./abis/FilecoinWarmStorageService.json
eventHandlers:
- event: DataSetCreated(indexed uint256,indexed uint256,uint256,uint256,uint256,address,address,address,string[],string[])
handler: handleFwssDataSetCreated
- event: PieceAdded(indexed uint256,indexed uint256,(bytes),string[],string[])
handler: handleFwssPieceAdded
- event: ServiceTerminated(indexed address,indexed uint256,uint256,uint256,uint256)
handler: handleFwssServiceTerminated
- event: PDPPaymentTerminated(indexed uint256,uint256,uint256)
handler: handleFwssPdpPaymentTerminated
- event: DataSetServiceProviderChanged(indexed uint256,indexed address,indexed address)
handler: handleFwssDataSetServiceProviderChanged
file: ./src/fwss.ts
29 changes: 29 additions & 0 deletions subgraph/subgraph_testnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,32 @@ dataSources:
- event: NextProvingPeriod(indexed uint256,uint256,uint256)
handler: handleNextProvingPeriod
file: ./src/pdp-verifier.ts
- kind: ethereum
name: FilecoinWarmStorageService
network: filecoin-testnet
source:
address: "0x02925630df557F957f70E112bA06e50965417CA0"
abi: FilecoinWarmStorageService
startBlock: 3141276
mapping:
kind: ethereum/events
apiVersion: 0.0.9
language: wasm/assemblyscript
entities:
- DataSet
- Root
abis:
- name: FilecoinWarmStorageService
file: ./abis/FilecoinWarmStorageService.json
eventHandlers:
- event: DataSetCreated(indexed uint256,indexed uint256,uint256,uint256,uint256,address,address,address,string[],string[])
handler: handleFwssDataSetCreated
- event: PieceAdded(indexed uint256,indexed uint256,(bytes),string[],string[])
handler: handleFwssPieceAdded
- event: ServiceTerminated(indexed address,indexed uint256,uint256,uint256,uint256)
handler: handleFwssServiceTerminated
- event: PDPPaymentTerminated(indexed uint256,uint256,uint256)
handler: handleFwssPdpPaymentTerminated
- event: DataSetServiceProviderChanged(indexed uint256,indexed address,indexed address)
handler: handleFwssDataSetServiceProviderChanged
file: ./src/fwss.ts
Loading
Loading