Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Exchange Oracle] Handle escrow_created webhook #1707

Merged
merged 32 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b06be7d
Handle escrow_created event in Exchange Oracle
flopez7 Mar 13, 2024
5346923
Fix exchange oracle tests
flopez7 Mar 13, 2024
db7bbe3
Merge branch 'develop' into feat/exchange/create-job
flopez7 Mar 14, 2024
fb76b76
Get jobs endpoint in Exchange Oracle (#1712)
flopez7 Mar 15, 2024
c7ede59
Modify jobDto to camelCase
flopez7 Mar 15, 2024
b378c7e
Check jobType filter in getJobList
flopez7 Mar 18, 2024
8233f75
Add create assignment and get assignments endpoints in Exchange Oracl…
flopez7 Mar 19, 2024
8dd5f13
[Fortune] Add endpoint for workers stats (#1717)
mrhouzlane Mar 19, 2024
77585bf
exchange oracle fixes
portuu3 Mar 19, 2024
d126128
comment cors
portuu3 Mar 20, 2024
0514bfd
replace npm by yarn
portuu3 Mar 20, 2024
9000d1a
Add cron job module (#1739)
flopez7 Mar 20, 2024
e443c56
update yarn.lock
portuu3 Mar 20, 2024
8e4ea65
Add endpoint to getOracleStats and optimize getAssignmentStats
flopez7 Mar 20, 2024
6ebe8a4
Fix database.module.ts path
flopez7 Mar 20, 2024
276fb45
add docker file to exo
portuu3 Mar 20, 2024
7f41d2a
Add heath module in Exchange Oracle
flopez7 Mar 20, 2024
598fcbe
test blueprint render
portuu3 Mar 20, 2024
92f9cfd
move render file to root folder
portuu3 Mar 20, 2024
b61bcac
fixes exchange oracle
portuu3 Mar 20, 2024
a539e78
Merge branch 'develop' into feat/exchange/create-job
flopez7 Mar 20, 2024
963ccc8
Fix job launcher webhook type and exchange oracle jwt auth
flopez7 Mar 21, 2024
d087794
Merge branch 'develop' into feat/exchange/create-job
flopez7 Mar 21, 2024
4481564
Fix case in signature guard
flopez7 Mar 21, 2024
c46b911
Fix outgoing webhook success check
flopez7 Mar 21, 2024
8dfbe1c
fix kyc status check and add constants
portuu3 Mar 21, 2024
201a7e6
Add exchange oracle unit tests
flopez7 Mar 22, 2024
4336c9b
Add API docs for exchange oracle and fix some bugs
flopez7 Mar 22, 2024
e370d4b
Fix exchange oracle webhook
flopez7 Mar 22, 2024
3ede6a3
remove unused render blueprint
portuu3 Mar 25, 2024
a57dc39
Merge branch 'develop' into feat/exchange/create-job
flopez7 Mar 25, 2024
d5cf385
delete unuseful test
portuu3 Mar 25, 2024
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 changes: 1 addition & 1 deletion packages/apps/dashboard/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"test": "vitest -u",
"format:prettier": "prettier --write '**/*.{ts,tsx}'",
"format:lint": "eslint --fix '**/*.{ts,tsx}'",
"format": "npm run format:prettier && npm run format:lint",
"format": "yarn format:prettier && yarn format:lint",
"vercel-build": "yarn workspace @human-protocol/sdk build && yarn build"
},
"browserslist": {
Expand Down
17 changes: 17 additions & 0 deletions packages/apps/fortune/exchange-oracle/server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Base image
FROM node:18

# Create app directory
WORKDIR /usr/src/app

# Bundle app source
COPY . .

# Install app dependencies
RUN yarn install

# Creates a "dist" folder with the production build
RUN yarn workspace @human-protocol/fortune-exchange-oracle-server build

# Start the server using the production build
CMD [ "node", "packages/apps/fortune/exchange-oracle/server/dist/src/main.js" ]
2 changes: 1 addition & 1 deletion packages/apps/fortune/exchange-oracle/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"start": "nest start",
"start:dev": "NODE_ENV=development nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"start:prod": "node dist/src/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"migration:create": "yarn build && typeorm-ts-node-commonjs migration:create",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Controller, Get, Redirect } from '@nestjs/common';
import { Public } from './common/decorators';
import { ApiExcludeController } from '@nestjs/swagger';
import { ApiTags, ApiExcludeController } from '@nestjs/swagger';

@Controller('/')
@ApiExcludeController()
@ApiTags('Main')
export class AppController {
@Public()
@Get('/')
@Redirect('/swagger', 301)
public health(): string {
return 'OK';
}
public redirect(): void {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import { WebhookModule } from './modules/webhook/webhook.module';
import { JwtAuthGuard } from './common/guards/jwt.auth';
import { JwtHttpStrategy } from './common/guards/strategy';
import { Web3Module } from './modules/web3/web3.module';
import { StatsModule } from './modules/stats/stats.module';
import { AssignmentModule } from './modules/assignment/assignment.module';
import { CronJobModule } from './modules/cron-job/cron-job.module';
import { HealthModule } from './modules/health/health.module';

@Module({
providers: [
Expand All @@ -24,9 +28,13 @@ import { Web3Module } from './modules/web3/web3.module';
JwtHttpStrategy,
],
imports: [
HealthModule,
AssignmentModule,
JobModule,
WebhookModule,
Web3Module,
StatsModule,
CronJobModule,
ConfigModule.forRoot({
envFilePath: process.env.NODE_ENV
? `.env.${process.env.NODE_ENV as string}`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const ConfigNames = {
HOST: 'HOST',
PORT: 'PORT',
WEB3_ENV: 'WEB3_ENV',
POSTGRES_URL: 'POSTGRES_URL',
POSTGRES_HOST: 'POSTGRES_HOST',
POSTGRES_USER: 'POSTGRES_USER',
POSTGRES_PASSWORD: 'POSTGRES_PASSWORD',
Expand All @@ -13,6 +14,7 @@ export const ConfigNames = {
POSTGRES_SYNC: 'POSTGRES_SYNC',
POSTGRES_SSL: 'POSTGRES_SSL',
POSTGRES_LOGGING: 'POSTGRES_LOGGING',
MAX_RETRY_COUNT: 'MAX_RETRY_COUNT',
WEB3_PRIVATE_KEY: 'WEB3_PRIVATE_KEY',
S3_ENDPOINT: 'S3_ENDPOINT',
S3_PORT: 'S3_PORT',
Expand All @@ -23,21 +25,24 @@ export const ConfigNames = {
PGP_ENCRYPT: 'PGP_ENCRYPT',
PGP_PRIVATE_KEY: 'ENCRYPTION_PRIVATE_KEY',
PGP_PASSPHRASE: 'PGP_PASSPHRASE',
CRON_SECRET: 'CRON_SECRET',
};

export const envValidator = Joi.object({
// General
NODE_ENV: Joi.string().default('development'),
HOST: Joi.string().default('localhost'),
PORT: Joi.string().default(3002),
MAX_RETRY_COUNT: Joi.number().default(5),
WEB3_ENV: Joi.string().default('testnet'),
// Database
DB_TYPE: Joi.string().default('postgres'),
POSTGRES_HOST: Joi.string().default('127.0.0.1'),
POSTGRES_USER: Joi.string().default('operator'),
POSTGRES_PASSWORD: Joi.string().default('qwerty'),
POSTGRES_DATABASE: Joi.string().default('reputation-oracle'),
POSTGRES_PORT: Joi.string().default('5432'),
POSTGRES_URL: Joi.string().optional(),
POSTGRES_HOST: Joi.string().optional(),
POSTGRES_USER: Joi.string().optional(),
POSTGRES_PASSWORD: Joi.string().optional(),
POSTGRES_DATABASE: Joi.string().optional(),
POSTGRES_PORT: Joi.string().optional(),
POSTGRES_SYNC: Joi.string().default('false'),
POSTGRES_SSL: Joi.string().default('false'),
POSTGRES_LOGGING: Joi.string(),
Expand All @@ -53,4 +58,6 @@ export const envValidator = Joi.object({
PGP_ENCRYPT: Joi.boolean().default(false),
PGP_PRIVATE_KEY: Joi.string().optional(),
PGP_PASSPHRASE: Joi.string().optional(),
// Cron Job Secret
CRON_SECRET: Joi.string().optional(),
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,21 @@
export enum ErrorWeb3 {
InvalidChainId = 'Invalid chain id provided for the configured environment',
}

/**
* Represents error messages associated to webhook.
*/
export enum ErrorWebhook {
NotSent = 'Webhook was not sent',
NotFound = 'Webhook not found',
UrlNotFound = 'Webhook URL not found',
NotCreated = 'Webhook has not been created',
}

/**
* Represents error messages associated with a cron job.
*/
export enum ErrorCronJob {
NotCompleted = 'Cron job is not completed',
Completed = 'Cron job is completed',
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ChainId } from '@human-protocol/sdk';

export const HEADER_SIGNATURE_KEY = 'human-signature';
export const ESCROW_FAILED_ENDPOINT = '/job/escrow-failed-webhook';
export const NS = 'hmt';
export const TOKEN = 'HMT';
export const DEFAULT_MAX_RETRY_COUNT = 5;

export const LOCALHOST_CHAIN_IDS = [ChainId.LOCALHOST];

Expand All @@ -16,3 +17,6 @@ export const MAINNET_CHAIN_IDS = [
ChainId.BSC_MAINNET,
ChainId.MOONBEAM,
];

export const JWT_KVSTORE_KEY = 'jwt_public_key';
export const KYC_APPROVED = 'APPROVED';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum SortDirection {
ASC = 'ASC',
DESC = 'DESC',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum CronJobType {
ProcessPendingWebhook = 'process-pending-webhook',
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ export enum JobStatus {
CANCELED = 'CANCELED',
}

export enum JobSortField {
CHAIN_ID = 'chain_id',
JOB_TYPE = 'job_type',
REWARD_AMOUNT = 'reward_amount',
CREATED_AT = 'created_at',
}

export enum JobFieldName {
JobDescription = 'job_description',
RewardAmount = 'reward_amount',
RewardToken = 'reward_token',
CreatedAt = 'created_at',
}

export enum AssignmentStatus {
ACTIVE = 'ACTIVE',
VALIDATION = 'VALIDATION',
Expand All @@ -12,3 +26,16 @@ export enum AssignmentStatus {
CANCELED = 'CANCELED',
REJECTED = 'REJECTED',
}

export enum AssignmentSortField {
CHAIN_ID = 'chain_id',
JOB_TYPE = 'job_type',
STATUS = 'status',
REWARD_AMOUNT = 'reward_amount',
CREATED_AT = 'created_at',
EXPIRES_AT = 'expires_at',
}

export enum JobType {
FORTUNE = 'FORTUNE',
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export enum EventType {
ESCROW_CANCELED = 'escrow_canceled',
TASK_CREATION_FAILED = 'task_creation_failed',
SUBMISSION_REJECTED = 'submission_rejected',
SUBMISSION_IN_REVIEW = 'submission_in_review',
}

export enum WebhookStatus {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ConfigNames } from '../config';

@Injectable()
export class CronAuthGuard implements CanActivate {
constructor(private readonly configService: ConfigService) {}

public async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const cronSecret = this.configService.get(ConfigNames.CRON_SECRET);
if (
cronSecret &&
request.headers['authorization'] === `Bearer ${cronSecret}`
) {
throw new UnauthorizedException('Unauthorized');
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ describe('SignatureAuthGuard', () => {
it('should return true if signature is verified', async () => {
mockRequest.headers[HEADER_SIGNATURE_KEY] = 'validSignature';
mockRequest.body = {
escrowAddress: MOCK_ADDRESS,
chainId: ChainId.LOCALHOST,
escrow_address: MOCK_ADDRESS,
chain_id: ChainId.LOCALHOST,
};
(verifySignature as jest.Mock).mockReturnValue(true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export class SignatureAuthGuard implements CanActivate {
const oracleAdresses: string[] = [];
try {
const escrowData = await EscrowUtils.getEscrow(
data.chainId,
data.escrowAddress,
data.chain_id,
data.escrow_address,
);
if (this.role.includes(Role.JobLauncher))
oracleAdresses.push(escrowData.launcher);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { ExtractJwt, Strategy } from 'passport-jwt';

import { KVStoreClient, StorageClient } from '@human-protocol/sdk';
import * as jwt from 'jsonwebtoken';
import { Web3Service } from 'src/modules/web3/web3.service';
import { Web3Service } from '../../../modules/web3/web3.service';
import { JwtUser } from '../../../common/types/jwt';
import { JWT_KVSTORE_KEY, KYC_APPROVED } from '../../../common/constant';

@Injectable()
export class JwtHttpStrategy extends PassportStrategy(Strategy, 'jwt-http') {
Expand All @@ -26,7 +28,7 @@ export class JwtHttpStrategy extends PassportStrategy(Strategy, 'jwt-http') {

const url = await kvstoreClient.getFileUrlAndVerifyHash(
(payload as any).reputation_network,
'jwt_public_key',
JWT_KVSTORE_KEY,
);
const publicKey = await StorageClient.downloadFileFromUrl(url);

Expand All @@ -42,14 +44,24 @@ export class JwtHttpStrategy extends PassportStrategy(Strategy, 'jwt-http') {

public async validate(
@Req() request: any,
payload: { email: string; address: string; kyc_status: string },
): Promise<any> {
payload: {
email: string;
address: string;
kyc_status: string;
reputation_network: string;
},
): Promise<JwtUser> {
if (!payload.kyc_status || !payload.email || !payload.address) {
throw new UnauthorizedException('Invalid token');
}
if (payload.kyc_status !== 'approved') {
if (payload.kyc_status !== KYC_APPROVED) {
throw new UnauthorizedException('Invalid KYC status');
}
return true;
return {
address: payload.address,
email: payload.email,
kycStatus: payload.kyc_status,
reputationNetwork: payload.reputation_network,
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface IBase {
id: number;
createdAt: Date;
updatedAt: Date;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { CronJobType } from '../enums/cron-job';
import { IBase } from './base';

export interface ICronJob extends IBase {
cronJobType: CronJobType;
startedAt: Date;
completedAt?: Date | null;
}
Loading
Loading