Skip to content

Commit 71308e6

Browse files
authored
[Reputation Oracle][Abuse] Modify abuse flow (#3646)
1 parent 82b1d0d commit 71308e6

File tree

11 files changed

+71
-297
lines changed

11 files changed

+71
-297
lines changed

docker-setup/docker-compose.dev.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ services:
7373

7474
graph-node-db:
7575
container_name: hp-dev-graph-node-db
76-
image: postgres:latest
76+
image: postgres:16
7777
restart: *default-restart
7878
logging:
7979
<<: *default-logging

packages/apps/fortune/exchange-oracle/server/src/common/enums/job.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
export enum JobStatus {
22
ACTIVE = 'active',
3-
PAUSED = 'paused',
43
COMPLETED = 'completed',
54
CANCELED = 'canceled',
65
}

packages/apps/fortune/exchange-oracle/server/src/modules/assignment/assignment.service.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ describe('AssignmentService', () => {
229229
id: 1,
230230
manifestUrl: MOCK_MANIFEST_URL,
231231
reputationNetwork: reputationNetwork,
232-
status: JobStatus.PAUSED,
232+
status: JobStatus.CANCELED,
233233
} as any);
234234

235235
await expect(

packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.spec.ts

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -667,54 +667,4 @@ describe('JobService', () => {
667667
).rejects.toThrow(`Solution not found in Escrow: ${escrowAddress}`);
668668
});
669669
});
670-
671-
describe('pauseJob', () => {
672-
const webhook: WebhookDto = {
673-
chainId,
674-
escrowAddress,
675-
eventType: EventType.ABUSE_DETECTED,
676-
};
677-
678-
it('should create a new job in the database', async () => {
679-
jest
680-
.spyOn(jobRepository, 'findOneByChainIdAndEscrowAddress')
681-
.mockResolvedValue({
682-
chainId: chainId,
683-
escrowAddress: escrowAddress,
684-
status: JobStatus.ACTIVE,
685-
} as JobEntity);
686-
const result = await jobService.pauseJob(webhook);
687-
688-
expect(result).toEqual(undefined);
689-
expect(jobRepository.updateOne).toHaveBeenCalledWith({
690-
chainId: chainId,
691-
escrowAddress: escrowAddress,
692-
status: JobStatus.PAUSED,
693-
});
694-
});
695-
696-
it('should fail if job not exists', async () => {
697-
jest
698-
.spyOn(jobRepository, 'findOneByChainIdAndEscrowAddress')
699-
.mockResolvedValue(null);
700-
701-
await expect(jobService.pauseJob(webhook)).rejects.toThrow(
702-
ErrorJob.NotFound,
703-
);
704-
});
705-
706-
it('should fail if job is not in Active status', async () => {
707-
jest
708-
.spyOn(jobRepository, 'findOneByChainIdAndEscrowAddress')
709-
.mockResolvedValue({
710-
chainId: chainId,
711-
escrowAddress: escrowAddress,
712-
status: JobStatus.CANCELED,
713-
} as JobEntity);
714-
715-
await expect(jobService.pauseJob(webhook)).rejects.toThrow(
716-
ErrorJob.InvalidStatus,
717-
);
718-
});
719-
});
720670
});

packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.ts

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -382,34 +382,4 @@ export class JobService {
382382

383383
return manifest;
384384
}
385-
386-
public async pauseJob(webhook: WebhookDto): Promise<void> {
387-
const jobEntity = await this.jobRepository.findOneByChainIdAndEscrowAddress(
388-
webhook.chainId,
389-
webhook.escrowAddress,
390-
);
391-
if (!jobEntity) {
392-
throw new ServerError(ErrorJob.NotFound);
393-
}
394-
if (jobEntity.status !== JobStatus.ACTIVE) {
395-
throw new ConflictError(ErrorJob.InvalidStatus);
396-
}
397-
jobEntity.status = JobStatus.PAUSED;
398-
await this.jobRepository.updateOne(jobEntity);
399-
}
400-
401-
public async resumeJob(webhook: WebhookDto): Promise<void> {
402-
const jobEntity = await this.jobRepository.findOneByChainIdAndEscrowAddress(
403-
webhook.chainId,
404-
webhook.escrowAddress,
405-
);
406-
if (!jobEntity) {
407-
throw new ServerError(ErrorJob.NotFound);
408-
}
409-
if (jobEntity.status !== JobStatus.PAUSED) {
410-
throw new ConflictError(ErrorJob.InvalidStatus);
411-
}
412-
jobEntity.status = JobStatus.ACTIVE;
413-
await this.jobRepository.updateOne(jobEntity);
414-
}
415385
}

packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.spec.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ describe('WebhookService', () => {
169169
});
170170

171171
it('should handle an incoming escrow abuse webhook', async () => {
172-
jest.spyOn(jobService, 'pauseJob').mockResolvedValue();
172+
jest.spyOn(jobService, 'cancelJob').mockResolvedValue();
173173
const webhook: WebhookDto = {
174174
chainId,
175175
escrowAddress,
@@ -178,16 +178,6 @@ describe('WebhookService', () => {
178178
expect(await webhookService.handleWebhook(webhook)).toBe(undefined);
179179
});
180180

181-
it('should handle an incoming escrow resume webhook', async () => {
182-
jest.spyOn(jobService, 'resumeJob').mockResolvedValue();
183-
const webhook: WebhookDto = {
184-
chainId,
185-
escrowAddress,
186-
eventType: EventType.ABUSE_DISMISSED,
187-
};
188-
expect(await webhookService.handleWebhook(webhook)).toBe(undefined);
189-
});
190-
191181
it('should return an error when the event type is invalid', async () => {
192182
const webhook: WebhookDto = {
193183
chainId,

packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,8 @@ export class WebhookService {
5454
break;
5555

5656
case EventType.ABUSE_DETECTED:
57-
await this.jobService.pauseJob(webhook);
58-
break;
59-
60-
case EventType.ABUSE_DISMISSED:
61-
await this.jobService.resumeJob(webhook);
57+
await this.jobService.cancelJob(webhook);
6258
break;
63-
6459
default:
6560
throw new ValidationError(
6661
`Invalid webhook event type: ${webhook.eventType}`,

packages/apps/job-launcher/server/src/modules/job/job.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ export class JobService {
147147
// DISABLE HMT
148148
if (
149149
requestType !== HCaptchaJobType.HCAPTCHA &&
150+
dto.chainId !== ChainId.LOCALHOST &&
150151
(dto.escrowFundToken === EscrowFundToken.HMT ||
151152
dto.paymentCurrency === PaymentCurrency.HMT)
152153
) {

packages/apps/job-launcher/server/src/modules/payment/payment.controller.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,10 @@ export class PaymentController {
452452
throw new ValidationError(ErrorPayment.InvalidChainId);
453453
}
454454

455+
if (chainId === ChainId.LOCALHOST) {
456+
return tokens;
457+
}
458+
455459
// Disable HMT
456460
const { [EscrowFundToken.HMT]: _omit, ...withoutHMT } = tokens;
457461

packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.spec.ts

Lines changed: 24 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -294,13 +294,6 @@ describe('AbuseService', () => {
294294
.mockResolvedValueOnce({
295295
exchangeOracle: fakeAddress,
296296
} as unknown as IEscrow);
297-
mockedOperatorUtils.getOperator
298-
.mockResolvedValueOnce({
299-
webhookUrl: webhookUrl1,
300-
} as IOperator)
301-
.mockResolvedValueOnce({
302-
webhookUrl: webhookUrl2,
303-
} as IOperator);
304297
mockAbuseSlackBot.sendAbuseNotification
305298
.mockResolvedValueOnce(undefined)
306299
.mockResolvedValueOnce(undefined);
@@ -325,9 +318,6 @@ describe('AbuseService', () => {
325318
mockedEscrowUtils.getEscrow.mockResolvedValueOnce({
326319
exchangeOracle: fakeAddress,
327320
} as unknown as IEscrow);
328-
mockedOperatorUtils.getOperator.mockResolvedValueOnce({
329-
webhookUrl: webhookUrl1,
330-
} as IOperator);
331321
mockAbuseRepository.findToClassify.mockResolvedValueOnce(
332322
mockAbuseEntities,
333323
);
@@ -345,32 +335,6 @@ describe('AbuseService', () => {
345335
});
346336
});
347337

348-
it('should handle errors when createOutgoingWebhook fails', async () => {
349-
const mockAbuseEntities = [generateAbuseEntity({ retriesCount: 0 })];
350-
351-
mockAbuseRepository.findToClassify.mockResolvedValueOnce(
352-
mockAbuseEntities,
353-
);
354-
mockedEscrowUtils.getEscrow.mockResolvedValueOnce({
355-
exchangeOracle: fakeAddress,
356-
} as unknown as IEscrow);
357-
mockedOperatorUtils.getOperator.mockResolvedValueOnce({
358-
webhookUrl: webhookUrl1,
359-
} as IOperator);
360-
361-
mockOutgoingWebhookService.createOutgoingWebhook.mockRejectedValueOnce(
362-
new DatabaseError('Failed to create webhook'),
363-
);
364-
365-
await abuseService.processAbuseRequests();
366-
367-
expect(mockAbuseRepository.findToClassify).toHaveBeenCalledTimes(1);
368-
expect(mockAbuseRepository.updateOne).toHaveBeenCalledWith({
369-
...mockAbuseEntities[0],
370-
retriesCount: 1,
371-
});
372-
});
373-
374338
it('should continue if createOutgoingWebhook throws a duplicated error', async () => {
375339
const mockAbuseEntities = [generateAbuseEntity(), generateAbuseEntity()];
376340

@@ -384,13 +348,6 @@ describe('AbuseService', () => {
384348
.mockResolvedValueOnce({
385349
exchangeOracle: fakeAddress,
386350
} as unknown as IEscrow);
387-
mockedOperatorUtils.getOperator
388-
.mockResolvedValueOnce({
389-
webhookUrl: webhookUrl1,
390-
} as IOperator)
391-
.mockResolvedValueOnce({
392-
webhookUrl: webhookUrl2,
393-
} as IOperator);
394351

395352
mockOutgoingWebhookService.createOutgoingWebhook.mockRejectedValueOnce(
396353
new DatabaseError(DatabaseErrorMessages.DUPLICATED),
@@ -450,24 +407,6 @@ describe('AbuseService', () => {
450407
retriesCount: 1,
451408
});
452409
});
453-
454-
it('should increment retries when operator data is missing', async () => {
455-
const abuseEntity = generateAbuseEntity({ retriesCount: 0 });
456-
mockAbuseRepository.findToClassify.mockResolvedValueOnce([abuseEntity]);
457-
458-
mockedEscrowUtils.getEscrow.mockResolvedValueOnce({
459-
exchangeOracle: fakeAddress,
460-
} as unknown as IEscrow);
461-
mockedOperatorUtils.getOperator.mockResolvedValueOnce(null);
462-
463-
await abuseService.processAbuseRequests();
464-
465-
expect(mockAbuseRepository.findToClassify).toHaveBeenCalledTimes(1);
466-
expect(mockAbuseRepository.updateOne).toHaveBeenCalledWith({
467-
...abuseEntity,
468-
retriesCount: 1,
469-
});
470-
});
471410
});
472411

473412
describe('processClassifiedAbuses', () => {
@@ -485,10 +424,15 @@ describe('AbuseService', () => {
485424
} as unknown as StakingClient);
486425
mockedEscrowUtils.getEscrow.mockResolvedValueOnce({
487426
launcher: fakeAddress,
427+
exchangeOracle: fakeAddress,
488428
} as unknown as IEscrow);
489-
mockedOperatorUtils.getOperator.mockResolvedValueOnce({
490-
webhookUrl: webhookUrl1,
491-
} as IOperator);
429+
mockedOperatorUtils.getOperator
430+
.mockResolvedValueOnce({
431+
webhookUrl: webhookUrl1,
432+
} as IOperator)
433+
.mockResolvedValueOnce({
434+
webhookUrl: webhookUrl2,
435+
} as IOperator);
492436
mockOutgoingWebhookService.createOutgoingWebhook.mockResolvedValueOnce(
493437
undefined,
494438
);
@@ -511,7 +455,17 @@ describe('AbuseService', () => {
511455
chainId: mockAbuseEntities[0].chainId,
512456
eventType: OutgoingWebhookEventType.ABUSE_DETECTED,
513457
},
514-
expect.any(String),
458+
webhookUrl1,
459+
);
460+
expect(
461+
mockOutgoingWebhookService.createOutgoingWebhook,
462+
).toHaveBeenCalledWith(
463+
{
464+
escrowAddress: mockAbuseEntities[0].escrowAddress,
465+
chainId: mockAbuseEntities[0].chainId,
466+
eventType: OutgoingWebhookEventType.ABUSE_DETECTED,
467+
},
468+
webhookUrl2,
515469
);
516470
expect(mockAbuseRepository.updateOne).toHaveBeenCalledWith({
517471
...mockAbuseEntities[0],
@@ -527,12 +481,6 @@ describe('AbuseService', () => {
527481
mockAbuseRepository.findClassified.mockResolvedValueOnce(
528482
mockAbuseEntities,
529483
);
530-
mockedEscrowUtils.getEscrow.mockResolvedValueOnce({
531-
exchangeOracle: fakeAddress,
532-
} as unknown as IEscrow);
533-
mockedOperatorUtils.getOperator.mockResolvedValueOnce({
534-
webhookUrl: webhookUrl1,
535-
} as IOperator);
536484

537485
await abuseService.processClassifiedAbuses();
538486

@@ -541,6 +489,11 @@ describe('AbuseService', () => {
541489
...mockAbuseEntities[0],
542490
status: AbuseStatus.COMPLETED,
543491
});
492+
expect(mockedEscrowUtils.getEscrow).not.toHaveBeenCalled();
493+
expect(mockedOperatorUtils.getOperator).not.toHaveBeenCalled();
494+
expect(
495+
mockOutgoingWebhookService.createOutgoingWebhook,
496+
).not.toHaveBeenCalled();
544497
});
545498

546499
it('should handle empty results from findClassified', async () => {
@@ -572,26 +525,5 @@ describe('AbuseService', () => {
572525
retriesCount: 1,
573526
});
574527
});
575-
576-
it('should increment retries when operator is missing (REJECTED)', async () => {
577-
const abuseEntity = generateAbuseEntity({
578-
retriesCount: 0,
579-
decision: AbuseDecision.REJECTED,
580-
});
581-
582-
mockAbuseRepository.findClassified.mockResolvedValueOnce([abuseEntity]);
583-
mockedEscrowUtils.getEscrow.mockResolvedValueOnce({
584-
exchangeOracle: fakeAddress,
585-
} as unknown as IEscrow);
586-
mockedOperatorUtils.getOperator.mockResolvedValueOnce(null);
587-
588-
await abuseService.processClassifiedAbuses();
589-
590-
expect(mockAbuseRepository.findClassified).toHaveBeenCalledTimes(1);
591-
expect(mockAbuseRepository.updateOne).toHaveBeenCalledWith({
592-
...abuseEntity,
593-
retriesCount: 1,
594-
});
595-
});
596528
});
597529
});

0 commit comments

Comments
 (0)