-
Notifications
You must be signed in to change notification settings - Fork 154
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1990 from privacy-scaling-explorations/feature/re…
…layer feat(relayer): add relayer service boilerplate
- Loading branch information
Showing
16 changed files
with
3,145 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
name: Relayer | ||
|
||
on: | ||
push: | ||
branches: [dev] | ||
pull_request: | ||
|
||
env: | ||
RPC_URL: "http://localhost:8545" | ||
|
||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} | ||
cancel-in-progress: true | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-22.04 | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: pnpm/action-setup@v4 | ||
with: | ||
version: 9 | ||
|
||
- name: Use Node.js 20 | ||
uses: actions/setup-node@v4 | ||
with: | ||
node-version: 20 | ||
cache: "pnpm" | ||
|
||
- name: Install | ||
run: | | ||
pnpm install --frozen-lockfile --prefer-offline | ||
- name: Build | ||
run: | | ||
pnpm run build | ||
- name: Run hardhat | ||
run: | | ||
pnpm run hardhat & | ||
sleep 5 | ||
working-directory: apps/relayer | ||
|
||
- name: Test | ||
run: pnpm run test:coverage | ||
working-directory: apps/relayer | ||
|
||
- name: Stop Hardhat | ||
if: always() | ||
run: kill $(lsof -t -i:8545) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Rate limit configuation | ||
TTL=60000 | ||
LIMIT=10 | ||
|
||
# Coordinator RPC url | ||
RELAYER_RPC_URL=http://localhost:8545 | ||
|
||
# Allowed origin host, use comma to separate each of them | ||
ALLOWED_ORIGINS= | ||
|
||
# Specify port for coordinator service (optional) | ||
PORT= | ||
|
||
# Mnemonic phrase | ||
MNEMONIC="" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
const path = require("path"); | ||
|
||
module.exports = { | ||
root: true, | ||
env: { | ||
node: true, | ||
jest: true, | ||
}, | ||
extends: ["../../.eslintrc.js"], | ||
parser: "@typescript-eslint/parser", | ||
parserOptions: { | ||
project: path.resolve(__dirname, "./tsconfig.json"), | ||
sourceType: "module", | ||
typescript: true, | ||
ecmaVersion: 2022, | ||
experimentalDecorators: true, | ||
requireConfigFile: false, | ||
ecmaFeatures: { | ||
classes: true, | ||
impliedStrict: true, | ||
}, | ||
warnOnUnsupportedTypeScriptVersion: true, | ||
}, | ||
overrides: [ | ||
{ | ||
files: ["./ts/**/*.module.ts"], | ||
rules: { | ||
"@typescript-eslint/no-extraneous-class": "off", | ||
}, | ||
}, | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
build/ | ||
coverage/ | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Relayer service | ||
|
||
## Instructions | ||
|
||
1. Add `.env` file (see `.env.example`). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
require("@nomicfoundation/hardhat-toolbox"); | ||
const dotenv = require("dotenv"); | ||
|
||
const path = require("path"); | ||
|
||
dotenv.config(); | ||
|
||
const parentDir = __dirname.includes("build") ? ".." : ""; | ||
const TEST_MNEMONIC = "test test test test test test test test test test test junk"; | ||
|
||
module.exports = { | ||
defaultNetwork: "hardhat", | ||
networks: { | ||
localhost: { | ||
url: process.env.RELAYER_RPC_URL || "", | ||
accounts: { | ||
mnemonic: process.env.MNEMONIC || TEST_MNEMONIC, | ||
path: "m/44'/60'/0'/0", | ||
initialIndex: 0, | ||
count: 20, | ||
}, | ||
loggingEnabled: false, | ||
}, | ||
hardhat: { | ||
loggingEnabled: false, | ||
}, | ||
}, | ||
paths: { | ||
sources: path.resolve(__dirname, parentDir, "./node_modules/maci-contracts/contracts"), | ||
artifacts: path.resolve(__dirname, parentDir, "./node_modules/maci-contracts/build/artifacts"), | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"$schema": "https://json.schemastore.org/nest-cli", | ||
"collection": "@nestjs/schematics", | ||
"sourceRoot": "ts", | ||
"compilerOptions": { | ||
"deleteOutDir": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
{ | ||
"name": "maci-relayer", | ||
"version": "0.1.0", | ||
"private": true, | ||
"description": "Relayer service for MACI", | ||
"main": "build/ts/main.js", | ||
"type": "module", | ||
"exports": { | ||
".": "./build/ts/main.js" | ||
}, | ||
"files": [ | ||
"build", | ||
"CHANGELOG.md", | ||
"README.md" | ||
], | ||
"scripts": { | ||
"hardhat": "hardhat node", | ||
"build": "nest build", | ||
"run:node": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));'", | ||
"start": "pnpm run run:node ./ts/main.ts", | ||
"start:prod": "pnpm run run:node build/ts/main.js", | ||
"test": "jest --forceExit", | ||
"test:coverage": "jest --coverage --forceExit", | ||
"types": "tsc -p tsconfig.json --noEmit" | ||
}, | ||
"dependencies": { | ||
"@nestjs/common": "^10.4.7", | ||
"@nestjs/core": "^10.4.7", | ||
"@nestjs/platform-express": "^10.4.7", | ||
"@nestjs/platform-socket.io": "^10.3.10", | ||
"@nestjs/swagger": "^8.0.3", | ||
"@nestjs/throttler": "^6.3.0", | ||
"@nestjs/websockets": "^10.4.7", | ||
"@nomicfoundation/hardhat-ethers": "^3.0.8", | ||
"@nomicfoundation/hardhat-toolbox": "^5.0.0", | ||
"class-transformer": "^0.5.1", | ||
"class-validator": "^0.14.1", | ||
"date-fns": "^4.1.0", | ||
"dotenv": "^16.4.5", | ||
"ethers": "^6.13.4", | ||
"hardhat": "^2.22.15", | ||
"helmet": "^8.0.0", | ||
"maci-contracts": "workspace:^2.5.0", | ||
"mustache": "^4.2.0", | ||
"reflect-metadata": "^0.2.0", | ||
"rxjs": "^7.8.1", | ||
"ts-node": "^10.9.1" | ||
}, | ||
"devDependencies": { | ||
"@nestjs/cli": "^10.4.2", | ||
"@nestjs/schematics": "^10.1.2", | ||
"@nestjs/testing": "^10.4.15", | ||
"@types/express": "^5.0.0", | ||
"@types/jest": "^29.5.2", | ||
"@types/node": "^22.10.5", | ||
"@types/supertest": "^6.0.2", | ||
"fast-check": "^3.23.1", | ||
"jest": "^29.5.0", | ||
"supertest": "^7.0.0", | ||
"ts-jest": "^29.2.5", | ||
"typescript": "^5.7.2" | ||
}, | ||
"jest": { | ||
"testTimeout": 900000, | ||
"moduleFileExtensions": [ | ||
"js", | ||
"json", | ||
"ts" | ||
], | ||
"rootDir": ".", | ||
"roots": [ | ||
"<rootDir>/ts", | ||
"<rootDir>/tests" | ||
], | ||
"testRegex": ".*\\.test\\.ts$", | ||
"transform": { | ||
"^.+\\.js$": [ | ||
"<rootDir>/ts/jest/transform.js", | ||
{ | ||
"useESM": true | ||
} | ||
], | ||
"^.+\\.(t|j)s$": [ | ||
"ts-jest", | ||
{ | ||
"useESM": true | ||
} | ||
] | ||
}, | ||
"collectCoverageFrom": [ | ||
"**/*.(t|j)s", | ||
"!<rootDir>/ts/main.ts", | ||
"!<rootDir>/ts/jest/*.js", | ||
"!<rootDir>/hardhat.config.js" | ||
], | ||
"coveragePathIgnorePatterns": [ | ||
"<rootDir>/ts/sessionKeys/__tests__/utils.ts" | ||
], | ||
"coverageDirectory": "<rootDir>/coverage", | ||
"testEnvironment": "node" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { HttpStatus, ValidationPipe, type INestApplication } from "@nestjs/common"; | ||
import { Test } from "@nestjs/testing"; | ||
import request from "supertest"; | ||
|
||
import type { App } from "supertest/types"; | ||
|
||
import { AppModule } from "../ts/app.module"; | ||
|
||
describe("e2e", () => { | ||
let app: INestApplication; | ||
|
||
beforeAll(async () => { | ||
const moduleFixture = await Test.createTestingModule({ | ||
imports: [AppModule], | ||
}).compile(); | ||
|
||
app = moduleFixture.createNestApplication(); | ||
app.useGlobalPipes(new ValidationPipe({ transform: true })); | ||
await app.listen(3000); | ||
}); | ||
|
||
afterAll(async () => { | ||
await app.close(); | ||
}); | ||
|
||
test("should throw an error if api is not found", async () => { | ||
const result = await request(app.getHttpServer() as App) | ||
.get("/unknown") | ||
.send() | ||
.expect(404); | ||
|
||
expect(result.body).toStrictEqual({ | ||
error: "Not Found", | ||
statusCode: HttpStatus.NOT_FOUND, | ||
message: "Cannot GET /unknown", | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { Module } from "@nestjs/common"; | ||
import { ThrottlerModule } from "@nestjs/throttler"; | ||
|
||
@Module({ | ||
imports: [ | ||
ThrottlerModule.forRoot([ | ||
{ | ||
ttl: Number(process.env.TTL), | ||
limit: Number(process.env.LIMIT), | ||
}, | ||
]), | ||
], | ||
}) | ||
export class AppModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import type { HardhatEthersHelpers } from "@nomicfoundation/hardhat-ethers/types"; | ||
import type { ethers } from "ethers"; | ||
|
||
declare module "hardhat/types/runtime" { | ||
interface HardhatRuntimeEnvironment { | ||
// We omit the ethers field because it is redundant. | ||
ethers: typeof ethers & HardhatEthersHelpers; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/* eslint-disable */ | ||
|
||
export function process(sourceText) { | ||
return { | ||
code: sourceText.replace("#!/usr/bin/env node", ""), | ||
}; | ||
} | ||
|
||
export default { | ||
process, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { ValidationPipe } from "@nestjs/common"; | ||
import { NestFactory } from "@nestjs/core"; | ||
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger"; | ||
import dotenv from "dotenv"; | ||
import helmet from "helmet"; | ||
|
||
import path from "path"; | ||
import url from "url"; | ||
|
||
/* eslint-disable no-underscore-dangle */ | ||
/* eslint-disable @typescript-eslint/no-shadow */ | ||
const __filename = url.fileURLToPath(import.meta.url); | ||
const __dirname = path.dirname(__filename); | ||
/* eslint-enable no-underscore-dangle */ | ||
/* eslint-disable @typescript-eslint/no-shadow */ | ||
|
||
dotenv.config({ | ||
path: [path.resolve(__dirname, "../.env"), path.resolve(__dirname, "../.env.example")], | ||
}); | ||
|
||
async function bootstrap() { | ||
const { AppModule } = await import("./app.module"); | ||
const app = await NestFactory.create(AppModule, { | ||
logger: ["log", "fatal", "error", "warn"], | ||
}); | ||
|
||
app.useGlobalPipes(new ValidationPipe({ transform: true })); | ||
app.use( | ||
helmet({ | ||
contentSecurityPolicy: { | ||
directives: { | ||
defaultSrc: [`'self'`], | ||
styleSrc: [`'self'`, `'unsafe-inline'`], | ||
imgSrc: [`'self'`, "data:", "validator.swagger.io"], | ||
scriptSrc: [`'self'`, `https: 'unsafe-inline'`], | ||
}, | ||
}, | ||
}), | ||
); | ||
app.enableCors({ origin: process.env.ALLOWED_ORIGINS?.split(",") }); | ||
|
||
const config = new DocumentBuilder() | ||
.setTitle("Relayer service") | ||
.setDescription("Relayer service API methods") | ||
.setVersion("1.0") | ||
.addTag("relayer") | ||
.addBearerAuth() | ||
.build(); | ||
const document = SwaggerModule.createDocument(app, config); | ||
SwaggerModule.setup("api", app, document); | ||
|
||
await app.listen(process.env.PORT || 3000); | ||
} | ||
|
||
bootstrap(); |
Oops, something went wrong.