diff --git a/packages/apps/fortune/exchange-oracle/server/src/common/enums/webhook.ts b/packages/apps/fortune/exchange-oracle/server/src/common/enums/webhook.ts index 3ccc451afc..da81b02ebb 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/common/enums/webhook.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/common/enums/webhook.ts @@ -2,7 +2,7 @@ export enum EventType { ESCROW_CREATED = 'escrow_created', ESCROW_COMPLETED = 'escrow_completed', ESCROW_CANCELED = 'escrow_canceled', - TASK_CREATION_FAILED = 'task_creation_failed', + ESCROW_FAILED = 'escrow_failed', SUBMISSION_REJECTED = 'submission_rejected', SUBMISSION_IN_REVIEW = 'submission_in_review', } diff --git a/packages/apps/fortune/exchange-oracle/server/src/database/migrations/1739274494282-escrowFailed.ts b/packages/apps/fortune/exchange-oracle/server/src/database/migrations/1739274494282-escrowFailed.ts new file mode 100644 index 0000000000..a4c1e8e924 --- /dev/null +++ b/packages/apps/fortune/exchange-oracle/server/src/database/migrations/1739274494282-escrowFailed.ts @@ -0,0 +1,60 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EscrowFailed1739274494282 implements MigrationInterface { + name = 'EscrowFailed1739274494282'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "hmt"."webhooks" + ADD "failure_detail" character varying + `); + await queryRunner.query(` + ALTER TYPE "hmt"."webhooks_event_type_enum" + RENAME TO "webhooks_event_type_enum_old" + `); + await queryRunner.query(` + CREATE TYPE "hmt"."webhooks_event_type_enum" AS ENUM( + 'escrow_created', + 'escrow_completed', + 'escrow_canceled', + 'escrow_failed', + 'submission_rejected', + 'submission_in_review' + ) + `); + await queryRunner.query(` + ALTER TABLE "hmt"."webhooks" + ALTER COLUMN "event_type" TYPE "hmt"."webhooks_event_type_enum" USING "event_type"::"text"::"hmt"."webhooks_event_type_enum" + `); + await queryRunner.query(` + DROP TYPE "hmt"."webhooks_event_type_enum_old" + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TYPE "hmt"."webhooks_event_type_enum_old" AS ENUM( + 'escrow_created', + 'escrow_completed', + 'escrow_canceled', + 'task_creation_failed', + 'submission_rejected', + 'submission_in_review' + ) + `); + await queryRunner.query(` + ALTER TABLE "hmt"."webhooks" + ALTER COLUMN "event_type" TYPE "hmt"."webhooks_event_type_enum_old" USING "event_type"::"text"::"hmt"."webhooks_event_type_enum_old" + `); + await queryRunner.query(` + DROP TYPE "hmt"."webhooks_event_type_enum" + `); + await queryRunner.query(` + ALTER TYPE "hmt"."webhooks_event_type_enum_old" + RENAME TO "webhooks_event_type_enum" + `); + await queryRunner.query(` + ALTER TABLE "hmt"."webhooks" DROP COLUMN "failure_detail" + `); + } +} diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.ts index 2d77b9abcf..c718a6bcd4 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.ts @@ -343,45 +343,43 @@ export class JobService { escrowAddress: string, manifestUrl: string, ): Promise { - const manifestEncrypted = - await StorageClient.downloadFileFromUrl(manifestUrl); + let manifest: ManifestDto | null = null; - let manifest: ManifestDto | null; - if ( - typeof manifestEncrypted === 'string' && - EncryptionUtils.isEncrypted(manifestEncrypted) - ) { - try { + try { + const manifestEncrypted = + await StorageClient.downloadFileFromUrl(manifestUrl); + + if ( + typeof manifestEncrypted === 'string' && + EncryptionUtils.isEncrypted(manifestEncrypted) + ) { const encryption = await Encryption.build( this.pgpConfigService.privateKey!, this.pgpConfigService.passphrase, ); - const decryptedData = await encryption.decrypt(manifestEncrypted); manifest = JSON.parse(Buffer.from(decryptedData).toString()); - } catch { - throw new Error(ErrorJob.ManifestDecryptionFailed); - } - } else { - try { + } else { manifest = typeof manifestEncrypted === 'string' ? JSON.parse(manifestEncrypted) : manifestEncrypted; - } catch { - manifest = null; } + } catch { + manifest = null; } if (!manifest) { const webhook = new WebhookEntity(); webhook.escrowAddress = escrowAddress; webhook.chainId = chainId; - webhook.eventType = EventType.TASK_CREATION_FAILED; + webhook.eventType = EventType.ESCROW_FAILED; + webhook.failureDetail = ErrorJob.ManifestNotFound; await this.webhookRepository.createUnique(webhook); - throw new NotFoundException(ErrorJob.ManifestNotFound); - } else return manifest; + } + + return manifest; } } diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.dto.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.dto.ts index 4cdee48a46..1e4e05c22d 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.dto.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.dto.ts @@ -23,13 +23,23 @@ export class RejectionEventData { public assignments: AssignmentRejection[]; } +export class FailedEventData { + @ApiProperty() + @IsString() + @IsOptional() + reason?: string; +} + export class SolutionEventData { @ApiProperty({ name: 'solutions_url' }) @IsString() solutionsUrl: string; } -export type EventData = RejectionEventData | SolutionEventData; +export type EventData = + | FailedEventData + | RejectionEventData + | SolutionEventData; export class WebhookDto { @ApiProperty({ diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.entity.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.entity.ts index 05f0d5d164..5107b238fe 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.entity.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.entity.ts @@ -30,4 +30,7 @@ export class WebhookEntity extends BaseEntity { enum: WebhookStatus, }) public status: WebhookStatus = WebhookStatus.PENDING; + + @Column({ type: 'varchar', nullable: true }) + public failureDetail: string; } diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.spec.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.spec.ts index 87d5fe8b9e..d3495eba17 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.spec.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.spec.ts @@ -171,10 +171,10 @@ describe('WebhookService', () => { const webhook: WebhookDto = { chainId, escrowAddress, - eventType: EventType.TASK_CREATION_FAILED, + eventType: EventType.ESCROW_FAILED, }; await expect(webhookService.handleWebhook(webhook)).rejects.toThrow( - 'Invalid webhook event type: task_creation_failed', + 'Invalid webhook event type: escrow_failed', ); }); }); @@ -253,7 +253,7 @@ describe('WebhookService', () => { const result = await (webhookService as any).getOracleWebhookUrl( JOB_LAUNCHER_WEBHOOK_URL, ChainId.LOCALHOST, - EventType.TASK_CREATION_FAILED, + EventType.ESCROW_FAILED, ); expect(result).toBe(JOB_LAUNCHER_WEBHOOK_URL); diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.ts index acde287478..771c8296d3 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.ts @@ -97,6 +97,11 @@ export class WebhookService { ), }; } + if (webhook.eventType === EventType.ESCROW_FAILED) { + webhookData.eventData = { + reason: webhook.failureDetail, + }; + } const transformedWebhook = CaseConverter.transformToSnakeCase(webhookData); const signedBody = await signMessage( @@ -154,7 +159,7 @@ export class WebhookService { const escrowClient = await EscrowClient.build(signer); let oracleAddress: string; switch (eventType) { - case EventType.TASK_CREATION_FAILED: + case EventType.ESCROW_FAILED: oracleAddress = await escrowClient.getJobLauncherAddress(escrowAddress); break; case EventType.SUBMISSION_IN_REVIEW: diff --git a/packages/apps/job-launcher/server/src/common/enums/webhook.ts b/packages/apps/job-launcher/server/src/common/enums/webhook.ts index 19fa24508d..e8875ac446 100644 --- a/packages/apps/job-launcher/server/src/common/enums/webhook.ts +++ b/packages/apps/job-launcher/server/src/common/enums/webhook.ts @@ -2,7 +2,6 @@ export enum EventType { ESCROW_CREATED = 'escrow_created', ESCROW_CANCELED = 'escrow_canceled', ESCROW_COMPLETED = 'escrow_completed', - TASK_CREATION_FAILED = 'task_creation_failed', ESCROW_FAILED = 'escrow_failed', ABUSE_DETECTED = 'abuse_detected', } diff --git a/packages/apps/job-launcher/server/src/database/migrations/1739194616649-escrowFailed.ts b/packages/apps/job-launcher/server/src/database/migrations/1739194616649-escrowFailed.ts new file mode 100644 index 0000000000..8c91177b03 --- /dev/null +++ b/packages/apps/job-launcher/server/src/database/migrations/1739194616649-escrowFailed.ts @@ -0,0 +1,64 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EscrowFailed1739194616649 implements MigrationInterface { + name = 'EscrowFailed1739194616649'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP INDEX "hmt"."IDX_012a8481fc9980fcc49f3f0dc2" + `); + await queryRunner.query(` + ALTER TYPE "hmt"."webhook_event_type_enum" + RENAME TO "webhook_event_type_enum_old" + `); + await queryRunner.query(` + CREATE TYPE "hmt"."webhook_event_type_enum" AS ENUM( + 'escrow_created', + 'escrow_canceled', + 'escrow_completed', + 'escrow_failed', + 'abuse_detected' + ) + `); + await queryRunner.query(` + ALTER TABLE "hmt"."webhook" + ALTER COLUMN "event_type" TYPE "hmt"."webhook_event_type_enum" USING "event_type"::"text"::"hmt"."webhook_event_type_enum" + `); + await queryRunner.query(` + DROP TYPE "hmt"."webhook_event_type_enum_old" + `); + await queryRunner.query(` + CREATE UNIQUE INDEX "IDX_012a8481fc9980fcc49f3f0dc2" ON "hmt"."webhook" ("chain_id", "escrow_address", "event_type") + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP INDEX "hmt"."IDX_012a8481fc9980fcc49f3f0dc2" + `); + await queryRunner.query(` + CREATE TYPE "hmt"."webhook_event_type_enum_old" AS ENUM( + 'escrow_created', + 'escrow_canceled', + 'escrow_completed', + 'task_creation_failed', + 'escrow_failed', + 'abuse_detected' + ) + `); + await queryRunner.query(` + ALTER TABLE "hmt"."webhook" + ALTER COLUMN "event_type" TYPE "hmt"."webhook_event_type_enum_old" USING "event_type"::"text"::"hmt"."webhook_event_type_enum_old" + `); + await queryRunner.query(` + DROP TYPE "hmt"."webhook_event_type_enum" + `); + await queryRunner.query(` + ALTER TYPE "hmt"."webhook_event_type_enum_old" + RENAME TO "webhook_event_type_enum" + `); + await queryRunner.query(` + CREATE UNIQUE INDEX "IDX_012a8481fc9980fcc49f3f0dc2" ON "hmt"."webhook" ("chain_id", "escrow_address", "event_type") + `); + } +} diff --git a/packages/apps/job-launcher/server/src/modules/job/job.service.ts b/packages/apps/job-launcher/server/src/modules/job/job.service.ts index 59bb117f4d..e242ae219c 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.service.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.service.ts @@ -1514,10 +1514,7 @@ export class JobService { } public async escrowFailedWebhook(dto: WebhookDataDto): Promise { - if ( - dto.eventType !== EventType.ESCROW_FAILED && - dto.eventType !== EventType.TASK_CREATION_FAILED - ) { + if (dto.eventType !== EventType.ESCROW_FAILED) { throw new ControlledError( ErrorJob.InvalidEventType, HttpStatus.BAD_REQUEST, diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts index 0f4f36ec73..cdc7c0629f 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts @@ -135,10 +135,6 @@ export class WebhookService { await this.jobService.completeJob(webhook); break; - case EventType.TASK_CREATION_FAILED: - await this.jobService.escrowFailedWebhook(webhook); - break; - case EventType.ESCROW_FAILED: await this.jobService.escrowFailedWebhook(webhook); break; diff --git a/packages/sdk/typescript/subgraph/config/localhost.json b/packages/sdk/typescript/subgraph/config/localhost.json index 8162fded9b..cbd3d5401b 100644 --- a/packages/sdk/typescript/subgraph/config/localhost.json +++ b/packages/sdk/typescript/subgraph/config/localhost.json @@ -2,12 +2,12 @@ "network": "localhost", "description": "Human subgraph on localhost", "EscrowFactory": { - "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", + "address": "0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9", "startBlock": 5, "abi": "../../../../node_modules/@human-protocol/core/abis/EscrowFactory.json" }, "HMToken": { - "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "address": "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0", "startBlock": 1, "abi": "../../../../node_modules/@human-protocol/core/abis/HMToken.json", "totalSupply": "1000000000000000000000000000", @@ -17,20 +17,15 @@ "abi": "../../../../node_modules/@human-protocol/core/abis/Escrow.json" }, "Staking": { - "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", + "address": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", "startBlock": 3, "abi": "../../../../node_modules/@human-protocol/core/abis/Staking.json" }, "KVStore": { - "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707", + "address": "0xdc64a140aa3e981100a9beca4e685f962f0cf6c9", "startBlock": 6, "abi": "../../../../node_modules/@human-protocol/core/abis/KVStore.json" }, - "RewardPool": { - "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853", - "startBlock": 8, - "abi": "../../../../node_modules/@human-protocol/core/abis/RewardPool.json" - }, "LegacyEscrow": { "abi": "../../../../node_modules/@human-protocol/core/abis/legacy/Escrow.json" }