Skip to content

Commit df7b351

Browse files
authored
Merge pull request #864 from humanprotocol/develop
Release 230904
2 parents 0f17aa2 + a4ad466 commit df7b351

File tree

117 files changed

+4194
-1050
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

117 files changed

+4194
-1050
lines changed

.github/workflows/cd-deploy-contracts.yaml

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -135,61 +135,4 @@ jobs:
135135
run: npx hardhat verify --network ${{ github.event.inputs.network }} ${{ steps.networks.outputs.reward_pool }}
136136
working-directory: ./packages/core
137137

138-
#Deploy subgraph
139-
- run: yarn global add @graphprotocol/graph-cli
140-
name: Install Graph CLI
141-
- run: graph auth --product hosted-service ${API_KEY}
142-
name: Authenticate Graph CLI
143-
env:
144-
API_KEY: ${{ secrets.HP_GRAPH_API_KEY }}
145-
- run: yarn generate
146-
name: Generate Subgraph
147-
working-directory: ./packages/sdk/typescript/subgraph
148-
env:
149-
NETWORK: ${{ steps.networks.outputs.subgraph }}
150-
- run: graph deploy --product hosted-service humanprotocol/${NETWORK}
151-
name: Deploy Subgraph
152-
working-directory: ./packages/sdk/typescript/subgraph
153-
env:
154-
NETWORK: ${{ steps.networks.outputs.subgraph }}
155-
#Commit changes to develop
156-
- name: Check for Changes
157-
if: always()
158-
id: check_changes
159-
run: |
160-
git fetch
161-
if [[ -n "$(git diff --name-only)" ]]; then
162-
echo "Changes detected."
163-
echo "::set-output name=changes::true"
164-
else
165-
echo "No changes detected."
166-
echo "::set-output name=changes::false"
167-
fi
168-
169-
- name: stash
170-
if: always() && steps.check_changes.outputs.changes == 'true'
171-
run: |
172-
git status
173-
git stash --include-untracked
174-
- name: Checkout develop
175-
if: always() && steps.check_changes.outputs.changes == 'true'
176-
uses: actions/checkout@v3
177-
with:
178-
ref: develop
179-
token: ${{ secrets.GH_TOKEN_CD_CONTRACTS }}
180-
- name: pop
181-
if: always() && steps.check_changes.outputs.changes == 'true'
182-
run: |
183-
git stash pop
184-
git status
185-
- name: Commit changes
186-
if: always() && steps.check_changes.outputs.changes == 'true'
187-
uses: EndBug/add-and-commit@v9
188-
with:
189-
add: "['./packages/core/.openzeppelin', './packages/sdk/typescript/subgraph/config']"
190-
message: 'Update grafting and upgrade file from CD'
191-
default_author: github_actions
192-
tag_push: '--force'
193-
github_token: ${{ secrets.GH_TOKEN_CD_CONTRACTS }}
194-
195138

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ export class JobService {
6161
};
6262

6363
this.storageClient = new StorageClient(
64-
storageCredentials,
6564
this.storageParams,
65+
storageCredentials,
6666
);
6767
}
6868

packages/apps/job-launcher/server/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ RECORDING_ORACLE_ADDRESS=
2626
REPUTATION_ORACLE_ADDRESS=
2727

2828
# Auth
29+
HASH_SECRET=a328af3fc1dad15342cc3d68936008fa
2930
JWT_SECRET=test-secret
3031
JWT_ACCESS_TOKEN_EXPIRES_IN=1d
3132
JWT_REFRESH_TOKEN_EXPIRES_IN=1d

packages/apps/job-launcher/server/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"start": "nest start",
1212
"start:dev": "nest start --watch",
1313
"start:debug": "nest start --debug --watch",
14-
"start:prod": "node dist/main",
14+
"start:prod": "node dist/src/main",
1515
"migration:create": "typeorm-ts-node-commonjs migration:create",
1616
"migration:generate": "yarn build && typeorm-ts-node-commonjs migration:generate -p -d typeorm.config.ts",
1717
"migration:revert": "typeorm-ts-node-commonjs migration:revert -d typeorm.config.ts",
@@ -24,7 +24,8 @@
2424
"test:watch": "jest --watch",
2525
"test:cov": "jest --coverage",
2626
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
27-
"test:e2e": "jest --config ./test/jest-e2e.json"
27+
"test:e2e": "jest --config ./test/jest-e2e.json",
28+
"vercel-build": "yarn workspace @human-protocol/sdk build"
2829
},
2930
"dependencies": {
3031
"@human-protocol/sdk": "*",
@@ -36,6 +37,7 @@
3637
"@nestjs/passport": "^10.0.0",
3738
"@nestjs/platform-express": "^9.4.3",
3839
"@nestjs/schedule": "^3.0.1",
40+
"@nestjs/serve-static": "^4.0.0",
3941
"@nestjs/swagger": "^7.0.6",
4042
"@nestjs/terminus": "^10.0.1",
4143
"@nestjs/typeorm": "^10.0.0",

packages/apps/job-launcher/server/src/app.module.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { JobModule } from './modules/job/job.module';
1313
import { PaymentModule } from './modules/payment/payment.module';
1414
import { Web3Module } from './modules/web3/web3.module';
1515
import { envValidator } from './common/config';
16+
import { ServeStaticModule } from '@nestjs/serve-static';
17+
import { join } from 'path';
1618

1719
@Module({
1820
providers: [
@@ -40,6 +42,10 @@ import { envValidator } from './common/config';
4042
JobModule,
4143
PaymentModule,
4244
Web3Module,
45+
ServeStaticModule.forRoot({
46+
rootPath: join(__dirname, '..', 'swagger-static'),
47+
serveRoot: process.env.NODE_ENV === 'development' ? '/' : '/swagger',
48+
}),
4349
],
4450
controllers: [AppController],
4551
})

packages/apps/job-launcher/server/src/common/config/env.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const ConfigNames = {
66
PORT: 'PORT',
77
FE_URL: 'FE_URL',
88
SESSION_SECRET: 'SESSION_SECRET',
9+
HASH_SECRET: 'HASH_SECRET',
910
JWT_SECRET: 'JWT_SECRET',
1011
JWT_ACCESS_TOKEN_EXPIRES_IN: 'JWT_ACCESS_TOKEN_EXPIRES_IN',
1112
JWT_REFRESH_TOKEN_EXPIRES_IN: 'JWT_REFRESH_TOKEN_EXPIRES_IN',
@@ -21,8 +22,10 @@ export const ConfigNames = {
2122
JOB_LAUNCHER_FEE: 'JOB_LAUNCHER_FEE',
2223
RECORDING_ORACLE_FEE: 'RECORDING_ORACLE_FEE',
2324
REPUTATION_ORACLE_FEE: 'REPUTATION_ORACLE_FEE',
24-
EXCHANGE_ORACLE_ADDRESS: 'EXCHANGE_ORACLE_ADDRESS',
25-
EXCHANGE_ORACLE_WEBHOOK_URL: 'EXCHANGE_ORACLE_WEBHOOK_URL',
25+
FORTUNE_EXCHANGE_ORACLE_ADDRESS: 'FORTUNE_EXCHANGE_ORACLE_ADDRESS',
26+
CVAT_EXCHANGE_ORACLE_ADDRESS: 'CVAT_EXCHANGE_ORACLE_ADDRESS',
27+
FORTUNE_EXCHANGE_ORACLE_WEBHOOK_URL: 'FORTUNE_EXCHANGE_ORACLE_WEBHOOK_URL',
28+
CVAT_EXCHANGE_ORACLE_WEBHOOK_URL: 'CVAT_EXCHANGE_ORACLE_WEBHOOK_URL',
2629
RECORDING_ORACLE_ADDRESS: 'RECORDING_ORACLE_ADDRESS',
2730
REPUTATION_ORACLE_ADDRESS: 'REPUTATION_ORACLE_ADDRESS',
2831
S3_ENDPOINT: 'S3_ENDPOINT',
@@ -39,6 +42,9 @@ export const ConfigNames = {
3942
SENDGRID_API_KEY: 'SENDGRID_API_KEY',
4043
SENDGRID_FROM_EMAIL: 'SENDGRID_FROM_EMAIL',
4144
SENDGRID_FROM_NAME: 'SENDGRID_FROM_NAME',
45+
CVAT_JOB_SIZE: 'CVAT_JOB_SIZE',
46+
CVAT_MAX_TIME: 'CVAT_MAX_TIME',
47+
CVAT_VAL_SIZE: 'CVAT_VAL_SIZE',
4248
};
4349

4450
export const envValidator = Joi.object({
@@ -49,7 +55,8 @@ export const envValidator = Joi.object({
4955
FE_URL: Joi.string().default('http://localhost:3005'),
5056
SESSION_SECRET: Joi.string().default('session_key'),
5157
// Auth
52-
JWT_SECRET: Joi.string().default('secrete'),
58+
HASH_SECRET: Joi.string().default('a328af3fc1dad15342cc3d68936008fa'),
59+
JWT_SECRET: Joi.string().default('secret'),
5360
JWT_ACCESS_TOKEN_EXPIRES_IN: Joi.string().default(1000000000),
5461
JWT_REFRESH_TOKEN_EXPIRES_IN: Joi.string().default(1000000000),
5562
// Database
@@ -67,8 +74,10 @@ export const envValidator = Joi.object({
6774
JOB_LAUNCHER_FEE: Joi.string().default(10),
6875
RECORDING_ORACLE_FEE: Joi.string().default(10),
6976
REPUTATION_ORACLE_FEE: Joi.string().default(10),
70-
EXCHANGE_ORACLE_ADDRESS: Joi.string().required(),
71-
EXCHANGE_ORACLE_WEBHOOK_URL: Joi.string().default('http://localhost:3005'),
77+
FORTUNE_EXCHANGE_ORACLE_ADDRESS: Joi.string().required(),
78+
CVAT_EXCHANGE_ORACLE_ADDRESS: Joi.string().required(),
79+
FORTUNE_EXCHANGE_ORACLE_WEBHOOK_URL: Joi.string().default('http://localhost:3004'),
80+
CVAT_EXCHANGE_ORACLE_WEBHOOK_URL: Joi.string().default('http://localhost:3005'),
7281
RECORDING_ORACLE_ADDRESS: Joi.string().required(),
7382
REPUTATION_ORACLE_ADDRESS: Joi.string().required(),
7483
// S3
@@ -88,4 +97,8 @@ export const envValidator = Joi.object({
8897
SENDGRID_API_KEY: Joi.string().required(),
8998
SENDGRID_FROM_EMAIL: Joi.string().default('job-launcher@hmt.ai'),
9099
SENDGRID_FROM_NAME: Joi.string().default('Human Protocol Job Launcher'),
100+
// CVAT
101+
CVAT_JOB_SIZE: Joi.string().default('10'),
102+
CVAT_MAX_TIME: Joi.string().default('300'),
103+
CVAT_VAL_SIZE: Joi.string().default('2'),
91104
});

packages/apps/job-launcher/server/src/common/constants/errors.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export enum ErrorJob {
1212
ResultValidationFailed = 'Result validation failed',
1313
InvalidRequestType = 'Invalid job type',
1414
JobParamsValidationFailed = 'Job parameters validation failed',
15+
InvalidEventType = 'Invalid event type',
16+
NotLaunched = 'Not launched'
1517
}
1618

1719
/**
@@ -21,6 +23,8 @@ export enum ErrorEscrow {
2123
NotFound = 'Escrow not found',
2224
NotCreated = 'Escrow has not been created',
2325
NotLaunched = 'Escrow has not been launched',
26+
InvalidStatusCancellation = 'Escrow has an invalid status for cancellation',
27+
InvalidBalanceCancellation = 'Escrow has an invalid balance for cancellation'
2428
}
2529

2630
/**
@@ -101,6 +105,14 @@ export enum ErrorSendGrid {
101105
InvalidApiKey = 'Invalid SendGrid API key',
102106
}
103107

108+
/**
109+
* Represents error messages related to signature.
110+
*/
111+
export enum ErrorSignature {
112+
SignatureNotVerified = 'Signature not verified',
113+
InvalidSignature = 'Invalid signature',
114+
}
115+
104116
/**
105117
* Represents error messages related to postgres.
106118
*/

packages/apps/job-launcher/server/src/common/constants/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@ export const MAINNET_CHAIN_IDS = [
2020

2121
export const SENDGRID_API_KEY_REGEX =
2222
/^SG\.[A-Za-z0-9-_]{22}\.[A-Za-z0-9-_]{43}$/;
23+
24+
export const HEADER_SIGNATURE_KEY = 'human-signature';

packages/apps/job-launcher/server/src/common/enums/job.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,23 @@ export enum JobStatus {
22
PENDING = 'PENDING',
33
PAID = 'PAID',
44
LAUNCHED = 'LAUNCHED',
5-
COMPLETED = 'COMPLETED',
65
FAILED = 'FAILED',
6+
TO_CANCEL = 'TO_CANCEL',
7+
CANCELED = 'CANCELED',
8+
}
9+
10+
export enum JobStatusFilter {
11+
PENDING = 'PENDING',
12+
PAID = 'PAID',
13+
LAUNCHED = 'LAUNCHED',
14+
FAILED = 'FAILED',
15+
TO_CANCEL = 'TO_CANCEL',
16+
CANCELED = 'CANCELED',
717
}
818

919
export enum JobRequestType {
1020
IMAGE_LABEL_BINARY = 'IMAGE_LABEL_BINARY',
21+
IMAGE_POINTS = 'IMAGE_POINTS',
22+
IMAGE_BOXES = 'IMAGE_BOXES',
1123
FORTUNE = 'FORTUNE',
1224
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export enum EventType {
2+
ESCROW_CREATED = 'escrow_created',
3+
ESCROW_CANCELED = 'escrow_canceled',
4+
TASK_CREATION_FAILED = 'task_creation_failed',
5+
}
6+
7+
export enum OracleType {
8+
FORTUNE = 'fortune',
9+
CVAT = 'cvat',
10+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './jwt.auth';
2+
export * from './signature.auth';
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { ExecutionContext, UnauthorizedException, BadRequestException } from '@nestjs/common';
3+
import { ConfigService } from '@nestjs/config';
4+
import { SignatureAuthGuard } from './signature.auth';
5+
import { verifySignature } from '../utils/signature';
6+
import { MOCK_ADDRESS } from '../../../test/constants';
7+
8+
jest.mock('../../common/utils/signature');
9+
10+
describe('SignatureAuthGuard', () => {
11+
let guard: SignatureAuthGuard;
12+
let mockConfigService: Partial<ConfigService>;
13+
14+
beforeEach(async () => {
15+
mockConfigService = {
16+
get: jest.fn(),
17+
};
18+
19+
const module: TestingModule = await Test.createTestingModule({
20+
providers: [
21+
SignatureAuthGuard,
22+
{ provide: ConfigService, useValue: mockConfigService }
23+
],
24+
}).compile();
25+
26+
guard = module.get<SignatureAuthGuard>(SignatureAuthGuard);
27+
});
28+
29+
it('should be defined', () => {
30+
expect(guard).toBeDefined();
31+
});
32+
33+
describe('canActivate', () => {
34+
let context: ExecutionContext;
35+
let mockRequest: any;
36+
37+
beforeEach(() => {
38+
mockRequest = {
39+
switchToHttp: jest.fn().mockReturnThis(),
40+
getRequest: jest.fn().mockReturnThis(),
41+
headers: {},
42+
body: {},
43+
originalUrl: '',
44+
};
45+
context = {
46+
switchToHttp: jest.fn().mockReturnThis(),
47+
getRequest: jest.fn(() => mockRequest)
48+
} as any as ExecutionContext;
49+
});
50+
51+
it('should return true if signature is verified', async () => {
52+
mockRequest.headers['header-signature-key'] = 'validSignature';
53+
jest.spyOn(guard, 'determineAddress').mockReturnValue('someAddress');
54+
(verifySignature as jest.Mock).mockReturnValue(true);
55+
56+
const result = await guard.canActivate(context as any);
57+
expect(result).toBeTruthy();
58+
});
59+
60+
it('should throw unauthorized exception if signature is not verified', async () => {
61+
jest.spyOn(guard, 'determineAddress').mockReturnValue('someAddress');
62+
(verifySignature as jest.Mock).mockReturnValue(false);
63+
64+
await expect(guard.canActivate(context as any)).rejects.toThrow(UnauthorizedException);
65+
});
66+
67+
it('should throw unauthorized exception for unrecognized oracle type', async () => {
68+
mockRequest.originalUrl = '/some/random/path';
69+
await expect(guard.canActivate(context as any)).rejects.toThrow(UnauthorizedException);
70+
});
71+
});
72+
73+
describe('determineAddress', () => {
74+
it('should return the correct address if originalUrl contains the fortune oracle type', () => {
75+
const mockRequest = { originalUrl: '/somepath/fortune/anotherpath' };
76+
const expectedAddress = MOCK_ADDRESS;
77+
mockConfigService.get = jest.fn().mockReturnValue(expectedAddress);
78+
79+
const result = guard.determineAddress(mockRequest);
80+
81+
expect(result).toEqual(expectedAddress);
82+
});
83+
84+
it('should return the correct address if originalUrl contains the cvat oracle type', () => {
85+
const mockRequest = { originalUrl: '/somepath/cvat/anotherpath' };
86+
const expectedAddress = MOCK_ADDRESS;
87+
mockConfigService.get = jest.fn().mockReturnValue(expectedAddress);
88+
89+
const result = guard.determineAddress(mockRequest);
90+
91+
expect(result).toEqual(expectedAddress);
92+
});
93+
94+
it('should throw BadRequestException for unrecognized oracle type', () => {
95+
const mockRequest = { originalUrl: '/some/random/path' };
96+
97+
expect(() => {
98+
guard.determineAddress(mockRequest);
99+
}).toThrow(BadRequestException);
100+
});
101+
102+
});
103+
});

0 commit comments

Comments
 (0)