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
1,434 changes: 1,434 additions & 0 deletions abis/v5/DataPermissionImplementation.json

Large diffs are not rendered by default.

1,188 changes: 1,188 additions & 0 deletions abis/v5/DataRegistryImplementation.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion config/moksha.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"vanaEpoch": true,
"dlpPerformance": true,
"queryEngine": true,
"dataRefinerRegistry": true
"dataRefinerRegistry": true,
"dataPermission": true
}
},
"initParams": {
Expand Down
3 changes: 2 additions & 1 deletion config/vana.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"vanaEpoch": true,
"dlpPerformance": true,
"queryEngine": true,
"dataRefinerRegistry": true
"dataRefinerRegistry": true,
"dataPermission": true
}
},
"initParams": {
Expand Down
59 changes: 59 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
type User @entity(immutable: false) {
id: ID!
fileContributions: [DataRegistryProof!] @derivedFrom(field: "user")
"All files owned by this user"
files: [File!]! @derivedFrom(field: "owner")
"All permissions granted by this user"
permissions: [Permission!]! @derivedFrom(field: "grantor")
"All servers trusted by this user"
trustedServers: [TrustedServer!]! @derivedFrom(field: "user")
}

type UserTotals @entity(immutable: false) {
Expand Down Expand Up @@ -130,3 +136,56 @@ type DlpPerformance @entity(immutable: false) {
createdTxHash: Bytes!
createdAtBlock: BigInt!
}

type File @entity(immutable: false) {
"The unique ID of the file, equivalent to the on-chain fileId."
id: ID!
"The owner of the file."
owner: User!
"The URL where the file data is stored (e.g., IPFS)."
url: String!
"The schema ID associated with this file, if any."
schemaId: BigInt!
"The block number when the file was added."
addedAtBlock: BigInt!
"The timestamp when the file was added."
addedAtTimestamp: BigInt!
"The transaction hash of the file addition."
transactionHash: Bytes!
}

type Permission @entity(immutable: false) {
"The unique ID of the permission, equivalent to the on-chain permissionId."
id: ID!
"The user who granted the permission."
grantor: User!
"The content identifier (e.g., IPFS URL) for the grant details."
grant: String!
"The nonce used for this permission grant."
nonce: BigInt!
"The signature provided by the user."
signature: Bytes!
"Whether the permission is currently active or has been revoked."
isActive: Boolean!
"File IDs associated with this permission."
fileIds: [BigInt!]!
"The block number when the permission was granted."
addedAtBlock: BigInt!
"The timestamp when the permission was granted."
addedAtTimestamp: BigInt!
"The transaction hash of the permission grant."
transactionHash: Bytes!
}

type TrustedServer @entity(immutable: false) {
"Composite ID: userAddress-serverAddress"
id: ID!
"The user who trusts the server."
user: User!
"The server's address (ID)."
serverAddress: Bytes!
"The URL of the server."
serverUrl: String!
"Timestamp of when the trust was established."
trustedAt: BigInt!
}
4 changes: 2 additions & 2 deletions src/lib/contract/shared.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Dlp, User} from "../../../generated/schema";
import {getOrCreateTotals, getTotalsIdDlp} from "../entity/totals";
import { Dlp, User } from "../../../generated/schema";
import { getOrCreateTotals, getTotalsIdDlp } from "../entity/totals";

export function getOrCreateUser(userId: string): User {
let user = User.load(userId);
Expand Down
22 changes: 19 additions & 3 deletions src/lib/contract/v1/data-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,43 @@ import { BigInt as GraphBigInt, log } from "@graphprotocol/graph-ts";

import {
DataRegistryProof,
EpochReference,
File,
FileOwner,
} from "../../../../generated/schema";

import {
FileAdded as FileAddedEvent,
ProofAdded as FileProofAdded,
} from "../../../../generated/DataRegistryImplementationV1/DataRegistryImplementationV1";
import { EPOCH_REFERENCE_ID_CURRENT } from "../../entity/epoch";
import { getEpochForBlock } from "../../entity/epoch"; // FIX: Added getEpochForBlock and EPOCH_REFERENCE_ID_CURRENT here
import {
getOrCreateUserTotals,
getUserTotalsId,
} from "../../entity/usertotals";
import { getOrCreateTotals, TOTALS_ID_GLOBAL } from "../../entity/totals";
import { getEpochForBlock } from "../../entity/epoch";
import { getOrCreateUser } from "../shared";

export function handleFileAddedV1(event: FileAddedEvent): void {
log.info("Handling DataRegistry FileAdded with transaction hash: {}", [
event.transaction.hash.toHex(),
]);

// Create user entity if it doesn't exist
const user = getOrCreateUser(event.params.ownerAddress.toHex());

// Create new File entity
const file = new File(event.params.fileId.toString());
file.owner = user.id;
file.url = event.params.url;
file.addedAtBlock = event.block.number;
file.addedAtTimestamp = event.block.timestamp;
file.transactionHash = event.transaction.hash;

// V1 of the contract does not support schemaId, so we set it to 0
file.schemaId = GraphBigInt.fromI32(0);

file.save();

const ownership = new FileOwner(event.params.fileId.toString());
ownership.ownerAddress = event.params.ownerAddress;
ownership.save();
Expand Down
31 changes: 27 additions & 4 deletions src/lib/contract/v2/data-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { BigInt as GraphBigInt, log } from "@graphprotocol/graph-ts";

import {
DataRegistryProof,
File,
FileOwner,
Dlp,
} from "../../../../generated/schema";

import {
Expand All @@ -20,13 +22,29 @@ import {
TOTALS_ID_GLOBAL,
} from "../../entity/totals";
import { getEpochForBlock } from "../../entity/epoch";
import {getOrCreateDlp} from "../shared";
import { getOrCreateUser } from "../shared";

export function handleFileAddedV2(event: FileAddedEvent): void {
log.info("Handling DataRegistry FileAdded with transaction hash: {}", [
event.transaction.hash.toHex(),
]);

// Create user entity if it doesn't exist
const user = getOrCreateUser(event.params.ownerAddress.toHex());

// Create new File entity
const file = new File(event.params.fileId.toString());
file.owner = user.id;
file.url = event.params.url;
file.addedAtBlock = event.block.number;
file.addedAtTimestamp = event.block.timestamp;
file.transactionHash = event.transaction.hash;

// V2 of the contract does not support schemaId, so we set it to 0
file.schemaId = GraphBigInt.fromI32(0);

file.save();

const ownership = new FileOwner(event.params.fileId.toString());
ownership.ownerAddress = event.params.ownerAddress;
ownership.save();
Expand All @@ -39,13 +57,18 @@ export function handleDataRegistryProofAddedV2(event: FileProofAdded): void {

// Get epoch for the current block
const epochId = getEpochForBlock(event.block.number);
if (!epochId) {
// FIX: Check for "-1" explicitly, as it is a truthy string
if (epochId == "-1") {
log.error("No epoch found for block {}", [event.block.number.toString()]);
return;
}

// Ensure the Dlp entity exists
const dlp = getOrCreateDlp(event.params.dlpId.toString());
// FIX: Load DLP instead of creating it to handle non-existent DLP case gracefully
const dlp = Dlp.load(event.params.dlpId.toString());
if (dlp == null) {
log.error("DLP not found for proof: {}", [event.params.dlpId.toString()]);
return;
}

// Create a new DataRegistryProof entity
const proof = new DataRegistryProof(event.transaction.hash.toHex());
Expand Down
32 changes: 26 additions & 6 deletions src/lib/contract/v3/data-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import { BigInt as GraphBigInt, log } from "@graphprotocol/graph-ts";
import {
DataRegistryProof,
Dlp,
EpochReference,
File,
FileOwner,
} from "../../../../generated/schema";

import {
FileAdded as FileAddedEvent,
ProofAdded as FileProofAdded,
} from "../../../../generated/DataRegistryImplementationV3/DataRegistryImplementationV3";
import { EPOCH_REFERENCE_ID_CURRENT } from "../../entity/epoch";
import {
getOrCreateUserTotals,
getUserTotalsId,
Expand All @@ -22,14 +21,30 @@ import {
getTotalsIdDlp,
TOTALS_ID_GLOBAL,
} from "../../entity/totals";
import {getOrCreateDlp, getOrCreateUser} from "../shared";
import { getOrCreateUser } from "../shared";
import { getEpochForBlock } from "../../entity/epoch";

export function handleFileAddedV3(event: FileAddedEvent): void {
log.info("Handling DataRegistry FileAdded with transaction hash: {}", [
event.transaction.hash.toHex(),
]);

// Create user entity if it doesn't exist
const user = getOrCreateUser(event.params.ownerAddress.toHex());

// Create new File entity
const file = new File(event.params.fileId.toString());
file.owner = user.id;
file.url = event.params.url;
file.addedAtBlock = event.block.number;
file.addedAtTimestamp = event.block.timestamp;
file.transactionHash = event.transaction.hash;

// V3 of the contract does not support schemaId, so we set it to 0
file.schemaId = GraphBigInt.fromI32(0);

file.save();

const ownership = new FileOwner(event.params.fileId.toString());
ownership.ownerAddress = event.params.ownerAddress;
ownership.save();
Expand All @@ -42,13 +57,18 @@ export function handleDataRegistryProofAddedV3(event: FileProofAdded): void {

// Get epoch for the current block
const epochId = getEpochForBlock(event.block.number);
if (!epochId) {
// Check for "-1" explicitly, as it is a truthy string
if (epochId == "-1") {
log.error("No epoch found for block {}", [event.block.number.toString()]);
return;
}

// Ensure the Dlp entity exists
const dlp = getOrCreateDlp(event.params.dlpId.toString());
// Load DLP instead of creating it to handle non-existent DLP case gracefully
const dlp = Dlp.load(event.params.dlpId.toString());
if (dlp == null) {
log.error("DLP not found for proof: {}", [event.params.dlpId.toString()]);
return;
}

// Create a new DataRegistryProof entity
const proof = new DataRegistryProof(event.transaction.hash.toHex());
Expand Down
114 changes: 114 additions & 0 deletions src/lib/contract/v5/data-permission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import {
log,
store,
BigInt as GraphBigInt,
Bytes,
} from "@graphprotocol/graph-ts";
import {
PermissionAdded,
PermissionRevoked,
ServerTrusted,
ServerUntrusted,
DataPermissionImplementation,
} from "../../../../generated/DataPermissionImplementation/DataPermissionImplementation";
import { Permission, TrustedServer } from "../../../../generated/schema";
import { getOrCreateUser } from "../shared";

export function handlePermissionAdded(event: PermissionAdded): void {
log.info("Handling PermissionAdded with transaction hash: {}", [
event.transaction.hash.toHexString(),
]);

const grantor = getOrCreateUser(event.params.user.toHex());
const permissionId = event.params.permissionId;

const permission = new Permission(permissionId.toString());
permission.grantor = grantor.id; // Renamed from 'user' to 'grantor'
permission.grant = event.params.grant;
permission.addedAtBlock = event.block.number;
permission.addedAtTimestamp = event.block.timestamp;
permission.transactionHash = event.transaction.hash;

// Set new fields
permission.isActive = true; // New permissions are active by default
permission.fileIds = event.params.fileIds; // Store the new fileIds array

// Since nonce and signature are not in the event, we must call the contract.
const contract = DataPermissionImplementation.bind(event.address);
const permissionData = contract.try_permissions(permissionId);

if (!permissionData.reverted) {
// The returned struct has a 'grantor' field, not 'user'
permission.nonce = permissionData.value.nonce;
permission.signature = permissionData.value.signature;
} else {
log.warning(
"Could not get permission data for id {}. Nonce and signature will be zero.",
[permissionId.toString()],
);
permission.nonce = GraphBigInt.zero();
permission.signature = new Bytes(0);
}

permission.save();
}

export function handlePermissionRevoked(event: PermissionRevoked): void {
log.info("Handling PermissionRevoked for permissionId: {}", [
event.params.permissionId.toString(),
]);

const permissionId = event.params.permissionId.toString();
const permission = Permission.load(permissionId);

if (permission) {
permission.isActive = false;
permission.save();
} else {
log.warning("Received revoke event for a permission not found in subgraph: {}", [
permissionId,
]);
}
}

export function handleServerTrusted(event: ServerTrusted): void {
log.info("Handling ServerTrusted for user {} and server {}", [
event.params.user.toHex(),
event.params.serverId.toHex(),
]);

const user = getOrCreateUser(event.params.user.toHex());
const serverId = event.params.serverId;
const compositeId = `${user.id}-${serverId.toHex()}`;

let trustedServer = TrustedServer.load(compositeId);
if (trustedServer == null) {
trustedServer = new TrustedServer(compositeId);
trustedServer.user = user.id;
trustedServer.serverAddress = serverId;
}

trustedServer.serverUrl = event.params.serverUrl;
trustedServer.trustedAt = event.block.timestamp;
trustedServer.save();
}

export function handleServerUntrusted(event: ServerUntrusted): void {
log.info("Handling ServerUntrusted for user {} and server {}", [
event.params.user.toHex(),
event.params.serverId.toHex(),
]);

const userId = event.params.user.toHex();
const serverId = event.params.serverId.toHex();
const compositeId = `${userId}-${serverId}`;

const trustedServer = TrustedServer.load(compositeId);
if (trustedServer != null) {
store.remove("TrustedServer", compositeId);
} else {
log.warning("Attempted to untrust a server that was not found: {}", [
compositeId,
]);
}
}
Loading
Loading