From 934f3abcb6ec8915bf74a6092493450ff7ed56ca Mon Sep 17 00:00:00 2001 From: eum602 Date: Sat, 15 Apr 2023 15:00:59 -0500 Subject: [PATCH 01/22] chore: add custom docker network --- docker-compose.yml | 16 +++++++++++++--- docs/tech/configuration.md | 4 ++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4246ed3..aeec44a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,16 +3,20 @@ services: postgres: image: postgres:latest ports: - - '${TYPEORM_PORT}:${TYPEORM_PORT}' + - '5433:5432' environment: POSTGRES_PASSWORD: postgres volumes: - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql + networks: + - backend redis: image: redis:latest command: redis-server --requirepass ${REDIS_PASSWORD} ports: - - '${REDIS_PORT}:${REDIS_PORT}' + - '6380:6379' + networks: + - backend node-api-base: build: context: ./ @@ -44,4 +48,10 @@ services: - redis volumes: - "./src:/app/src" - - /src/node_modules \ No newline at end of file + - /src/node_modules + networks: + - backend +networks: + backend: + name: backend + external: true \ No newline at end of file diff --git a/docs/tech/configuration.md b/docs/tech/configuration.md index fff25e5..3174a4f 100644 --- a/docs/tech/configuration.md +++ b/docs/tech/configuration.md @@ -64,6 +64,10 @@ In order to run the app with Docker, you should install or update to the latest The following commands will build and run all you need to start working on the base, without any other installation requirements. Important: if you already have postgres running locally, you'll need to kill the service before run `docker-compose up`. +``` +docker network create backend +``` + ``` docker-compose --env-file .env.dev build ``` From 64c55a5069fd9f0f8c5a82fc17f87cb71b494cb1 Mon Sep 17 00:00:00 2001 From: eum602 Date: Sat, 15 Apr 2023 15:44:43 -0500 Subject: [PATCH 02/22] feat: add controller to create a did 'web' compatible with 'lac' method --- .example.env | 6 +- .example.env.test | 4 + package.json | 1 + src/config/index.ts | 4 +- src/constants/errorMessages.ts | 3 +- src/controllers/did.controller.ts | 24 ++++++ src/controllers/index.ts | 4 +- src/entities/did.entity.ts | 10 +++ src/interfaces/key/key.interface.ts | 5 ++ src/services/did.service.ts | 79 ++++++++++++++++++++ src/services/external/key-manager.service.ts | 15 ++++ yarn.lock | 5 ++ 12 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 src/controllers/did.controller.ts create mode 100644 src/entities/did.entity.ts create mode 100644 src/interfaces/key/key.interface.ts create mode 100644 src/services/did.service.ts create mode 100644 src/services/external/key-manager.service.ts diff --git a/.example.env b/.example.env index c69f6e3..9065627 100644 --- a/.example.env +++ b/.example.env @@ -1,6 +1,6 @@ #PORT -PORT = 3000 +PORT = 3001 #DOCS @@ -49,3 +49,7 @@ SENDGRID_API_KEY= #Email EMAIL_TRANSPORTER = AWS + +# EXTERNAL SERVICES +KEY_MANAGER_BASE_URL=http://laccpass-key-manager/api/v1 +SECP256K1_KEY=/secp256k1 \ No newline at end of file diff --git a/.example.env.test b/.example.env.test index 32848c0..109266d 100644 --- a/.example.env.test +++ b/.example.env.test @@ -48,3 +48,7 @@ SENDGRID_API_KEY= #Email EMAIL_TRANSPORTER = AWS + +# EXTERNAL SERVICES +KEY_MANAGER_BASE_URL=http://laccpass-key-manager/api/v1 +SECP256K1_KEY=/secp256k1 diff --git a/package.json b/package.json index a31e2a6..84f5057 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ }, "dependencies": { "aws-sdk": "^2.1116.0", + "base-x": "^4.0.0", "bcrypt": "^5.1.0", "body-parser": "^1.20.0", "class-transformer": "^0.5.1", diff --git a/src/config/index.ts b/src/config/index.ts index 6c54ee1..18b094f 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -39,5 +39,7 @@ export const { AWS_SES_API_VERSION, DOCS_ENABLED, SENDGRID_API_USER, - SENDGRID_API_KEY + SENDGRID_API_KEY, + KEY_MANAGER_BASE_URL, + SECP256K1_KEY } = process.env; diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts index 5fd5878..3d7e2fe 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -18,7 +18,8 @@ export enum ErrorsMessages { // HTTP STANDARD MESSAGES INTERNAL_SERVER_ERROR = 'Internal Server Error', BAD_REQUEST_ERROR = 'Bad request error', - USER_ALREADY_EXISTS = 'A user with this email is already registered' + USER_ALREADY_EXISTS = 'A user with this email is already registered', + CREATE_KEY_ERROR = 'An internal server error occurred while trying to create a new key' } export const Errors = { diff --git a/src/controllers/did.controller.ts b/src/controllers/did.controller.ts new file mode 100644 index 0000000..10a1822 --- /dev/null +++ b/src/controllers/did.controller.ts @@ -0,0 +1,24 @@ +import { JsonController, Post, BadRequestError } from 'routing-controllers'; +import { Service } from 'typedi'; +import { EntityMapper } from '@clients/mapper/entityMapper.service'; +import { DidService } from '@services/did.service'; +import { Did } from '@entities/did.entity'; +import { ErrorsMessages } from '@constants/errorMessages'; + +@JsonController('/did') +@Service() +export class DidController { + constructor(private readonly didService: DidService) {} + + @Post('/web-lac') + async createDidWebLac(): Promise { + try { + const did = EntityMapper.mapTo(Did, {}); + return this.didService.createDidWebLac(did); + } catch (error: any) { + throw new BadRequestError( + error.detail ?? error.message ?? ErrorsMessages.INTERNAL_SERVER_ERROR + ); + } + } +} diff --git a/src/controllers/index.ts b/src/controllers/index.ts index 201f417..15c8654 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1 +1,3 @@ -export const controllers = []; +import { DidController } from './did.controller'; + +export const controllers = [DidController]; diff --git a/src/entities/did.entity.ts b/src/entities/did.entity.ts new file mode 100644 index 0000000..2381fb7 --- /dev/null +++ b/src/entities/did.entity.ts @@ -0,0 +1,10 @@ +import { Column, Entity } from 'typeorm'; +import { Base } from './base.entity'; +@Entity() +export class Did extends Base { + @Column({ unique: true }) + did!: string; + + @Column({ unique: true }) + keyId?: string; +} diff --git a/src/interfaces/key/key.interface.ts b/src/interfaces/key/key.interface.ts new file mode 100644 index 0000000..4197cf0 --- /dev/null +++ b/src/interfaces/key/key.interface.ts @@ -0,0 +1,5 @@ +export interface ISecp256k1 { + id: string; + keyId: string; + address: string; +} diff --git a/src/services/did.service.ts b/src/services/did.service.ts new file mode 100644 index 0000000..648a5ae --- /dev/null +++ b/src/services/did.service.ts @@ -0,0 +1,79 @@ +import { Service } from 'typedi'; +import { getRepository } from 'typeorm'; +import { Did } from '@entities/did.entity'; +import { KeyManagerService } from './external/key-manager.service'; +import { InternalServerError } from 'routing-controllers'; +import { ErrorsMessages } from '@constants/errorMessages'; +import { ISecp256k1 } from 'src/interfaces/key/key.interface'; + +@Service() +export class DidService { + private readonly didRepository = getRepository(Did); + private readonly base58 = require('base-x')( + '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' + ); + private readonly hex = require('base-x')('0123456789abcdef'); + + private readonly didEncodingVersion = '0001'; // constant for encoding + // eslint-disable-next-line max-len + private readonly didType = '0001'; // lac -> make a table for the different kinds of supported did types + private readonly chainId = '0x9e55c'; + private readonly lacchainHostname = 'lacchain.id'; + private readonly didLacWebIdentifier: string; + private readonly didRegistryAddress: string; + constructor(private keyManagerService: KeyManagerService) { + this.didLacWebIdentifier = `did:web:${this.lacchainHostname}:`; + this.didRegistryAddress = '0xBeB1df7Fb80CA88226aE1DaDa169639E950f3D79'; + } + + show(id: string) { + return this.didRepository.findOne(id); + } + + async createDidWebLac(did: Did) { + const result = await this.keyManagerService.createSecp256k1Key(); + if (result.status !== 200) { + console.log(await result.text()); + throw new InternalServerError(ErrorsMessages.CREATE_KEY_ERROR); + } + const key: ISecp256k1 = await result.json(); + did.keyId = key.keyId; + did.did = + this.didLacWebIdentifier + + this.encode( + this.didType, + this.chainId, + key.address, + this.didRegistryAddress + ); + await this.didRepository.insert(did); + return { did: did.did }; + } + // todo: validate parameters + encode( + didType: string, + chainId: string, + primaryAddress: string, + didRegistry: string + ): string { + const payload = [ + Buffer.from(this.didEncodingVersion, 'hex'), + Buffer.from(didType, 'hex'), + this.getLacchainDataBuffer(chainId, primaryAddress, didRegistry) + ]; + return this.base58.encode(Buffer.concat(payload)); + } + + getLacchainDataBuffer( + chainId: string, + address: string, + didRegistry: string + ): Buffer { + const dataArr = [ + this.hex.decode(chainId.slice(2), 'hex'), + Buffer.from(address.slice(2), 'hex'), + Buffer.from(didRegistry.slice(2), 'hex') + ]; + return Buffer.concat(dataArr); + } +} diff --git a/src/services/external/key-manager.service.ts b/src/services/external/key-manager.service.ts new file mode 100644 index 0000000..bbd502a --- /dev/null +++ b/src/services/external/key-manager.service.ts @@ -0,0 +1,15 @@ +import { Service } from 'typedi'; +import fetch from 'node-fetch'; +import { KEY_MANAGER_BASE_URL, SECP256K1_KEY } from '@config'; + +@Service() +export class KeyManagerService { + async createSecp256k1Key() { + return fetch(`${KEY_MANAGER_BASE_URL}${SECP256K1_KEY}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }); + } +} diff --git a/yarn.lock b/yarn.lock index b0990ed..312c88f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1485,6 +1485,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base-x@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" + integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== + base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" From 42b8eb409459aef72c06fb715d804a4c7f0db487 Mon Sep 17 00:00:00 2001 From: eum602 Date: Sat, 22 Apr 2023 13:45:04 -0500 Subject: [PATCH 03/22] chore: update database names --- .example.env | 2 +- docker_postgres_init.sql | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.example.env b/.example.env index 9065627..6c66b22 100644 --- a/.example.env +++ b/.example.env @@ -12,7 +12,7 @@ TYPEORM_TYPE = postgres TYPEORM_HOST = localhost TYPEORM_USERNAME = postgres TYPEORM_PASSWORD = postgres -TYPEORM_DATABASE = express_api_base_development +TYPEORM_DATABASE = laccpass_identity_development TYPEORM_PORT = 5432 TYPEORM_SYNCHRONIZE = false TYPEORM_LOGGING = true diff --git a/docker_postgres_init.sql b/docker_postgres_init.sql index 21721a5..50b4950 100644 --- a/docker_postgres_init.sql +++ b/docker_postgres_init.sql @@ -1,10 +1,10 @@ CREATE USER docker WITH PASSWORD 'password' CREATEDB; -CREATE DATABASE express_api_base_development +CREATE DATABASE laccpass_identity_development WITH OWNER = docker CONNECTION LIMIT = -1; -CREATE DATABASE express_api_base_test +CREATE DATABASE laccpass_identity WITH OWNER = docker CONNECTION LIMIT = -1; From 3c0e3b7d1f991b7857f30f1a5e95d8102bba8d1f Mon Sep 17 00:00:00 2001 From: eum602 Date: Sat, 22 Apr 2023 17:33:18 -0500 Subject: [PATCH 04/22] chore: configure exposed ports as environment variables --- .example.env | 5 ++++- docker-compose.yml | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.example.env b/.example.env index 6c66b22..5c392d8 100644 --- a/.example.env +++ b/.example.env @@ -1,6 +1,7 @@ #PORT -PORT = 3001 +PORT = 80 # use port 80 when running with docker +EXPOSED_CONTAINER_SERVER_PORT = 3001 #DOCS @@ -17,12 +18,14 @@ TYPEORM_PORT = 5432 TYPEORM_SYNCHRONIZE = false TYPEORM_LOGGING = true TYPEORM_MIGRATIONS_RUN = true +EXPOSED_CONTAINER_TYPEORM_PORT = 5433 #REDIS REDIS_HOST = redis REDIS_PORT = 6379 REDIS_PASSWORD = redis +EXPOSED_CONTAINER_REDIS_PORT = 6380 #TOKEN diff --git a/docker-compose.yml b/docker-compose.yml index aeec44a..8e2b9fe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,9 +3,9 @@ services: postgres: image: postgres:latest ports: - - '5433:5432' + - '${EXPOSED_CONTAINER_TYPEORM_PORT}:${TYPEORM_PORT}' environment: - POSTGRES_PASSWORD: postgres + POSTGRES_PASSWORD: ${TYPEORM_PASSWORD} volumes: - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql networks: @@ -14,7 +14,7 @@ services: image: redis:latest command: redis-server --requirepass ${REDIS_PASSWORD} ports: - - '6380:6379' + - '${EXPOSED_CONTAINER_REDIS_PORT}:${REDIS_PORT}' networks: - backend node-api-base: @@ -37,7 +37,7 @@ services: RATE_LIMIT_WINDOW: "${RATE_LIMIT_WINDOW}" RATE_LIMIT_MAX_REQUESTS: "${RATE_LIMIT_MAX_REQUESTS}" ports: - - '${PORT}:${PORT}' + - '${EXPOSED_CONTAINER_SERVER_PORT}:${PORT}' restart: on-failure container_name: laccpass-identity-manager depends_on: From 2bc197d18183de284ce76dd2d7bd02412a1d045d Mon Sep 17 00:00:00 2001 From: eum602 Date: Sat, 22 Apr 2023 21:29:45 -0500 Subject: [PATCH 05/22] chore: add support to use key manager either as a library or as an external service --- .example.env | 1 + ormconfig.ts | 11 ++++-- package.json | 1 + src/config/index.ts | 3 +- src/services/did.service.ts | 10 +----- src/services/external/key-manager.service.ts | 36 +++++++++++++++++--- yarn.lock | 36 ++++++++++++++++++++ 7 files changed, 82 insertions(+), 16 deletions(-) diff --git a/.example.env b/.example.env index 5c392d8..62e6d84 100644 --- a/.example.env +++ b/.example.env @@ -54,5 +54,6 @@ SENDGRID_API_KEY= EMAIL_TRANSPORTER = AWS # EXTERNAL SERVICES +# IS_INDEPENDENT_SERVICE = true # uncomment this only if the service communicates with the other components by way of external services KEY_MANAGER_BASE_URL=http://laccpass-key-manager/api/v1 SECP256K1_KEY=/secp256k1 \ No newline at end of file diff --git a/ormconfig.ts b/ormconfig.ts index 201050e..17c7997 100644 --- a/ormconfig.ts +++ b/ormconfig.ts @@ -9,9 +9,12 @@ import { TYPEORM_LOGGING, TYPEORM_MIGRATIONS_RUN, PRODUCTION_ENV, - TYPEORM_TYPE + TYPEORM_TYPE, + IS_INDEPENDENT_SERVICE } from '@config'; +import { Secp256k1 } from 'lacpass-key-manager'; + const config: ConnectionOptions = { type: TYPEORM_TYPE as 'mysql' | 'postgres' | 'mongodb', host: TYPEORM_HOST, @@ -20,7 +23,7 @@ const config: ConnectionOptions = { database: TYPEORM_DATABASE, port: Number.parseInt(TYPEORM_PORT || '5432'), synchronize: TYPEORM_SYNCHRONIZE === 'true', - logging: TYPEORM_LOGGING === 'true', + logging: TYPEORM_LOGGING === 'true' ? ['error'] : false, entities: [ PRODUCTION_ENV ? 'dist/src/entities/**/*.js' : 'src/entities/**/*.ts' ], @@ -38,4 +41,8 @@ const config: ConnectionOptions = { } }; +if (IS_INDEPENDENT_SERVICE !== 'true') { + config.entities?.push(Secp256k1); +} + export = config; diff --git a/package.json b/package.json index 84f5057..5ded602 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "express-rate-limit": "^6.3.0", "helmet": "^5.0.2", "jsonwebtoken": "^9.0.0", + "lacpass-key-manager": "^0.0.8", "morgan": "^1.10.0", "multer": "^1.4.4", "nodemailer": "^6.7.3", diff --git a/src/config/index.ts b/src/config/index.ts index 18b094f..aed7a04 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -41,5 +41,6 @@ export const { SENDGRID_API_USER, SENDGRID_API_KEY, KEY_MANAGER_BASE_URL, - SECP256K1_KEY + SECP256K1_KEY, + IS_INDEPENDENT_SERVICE } = process.env; diff --git a/src/services/did.service.ts b/src/services/did.service.ts index 648a5ae..8ec14de 100644 --- a/src/services/did.service.ts +++ b/src/services/did.service.ts @@ -2,9 +2,6 @@ import { Service } from 'typedi'; import { getRepository } from 'typeorm'; import { Did } from '@entities/did.entity'; import { KeyManagerService } from './external/key-manager.service'; -import { InternalServerError } from 'routing-controllers'; -import { ErrorsMessages } from '@constants/errorMessages'; -import { ISecp256k1 } from 'src/interfaces/key/key.interface'; @Service() export class DidService { @@ -31,12 +28,7 @@ export class DidService { } async createDidWebLac(did: Did) { - const result = await this.keyManagerService.createSecp256k1Key(); - if (result.status !== 200) { - console.log(await result.text()); - throw new InternalServerError(ErrorsMessages.CREATE_KEY_ERROR); - } - const key: ISecp256k1 = await result.json(); + const key = await this.keyManagerService.createSecp256k1Key(); did.keyId = key.keyId; did.did = this.didLacWebIdentifier + diff --git a/src/services/external/key-manager.service.ts b/src/services/external/key-manager.service.ts index bbd502a..24e6f47 100644 --- a/src/services/external/key-manager.service.ts +++ b/src/services/external/key-manager.service.ts @@ -1,15 +1,43 @@ import { Service } from 'typedi'; import fetch from 'node-fetch'; -import { KEY_MANAGER_BASE_URL, SECP256K1_KEY } from '@config'; - +import { + IS_INDEPENDENT_SERVICE, + KEY_MANAGER_BASE_URL, + SECP256K1_KEY +} from '@config'; +import { Secp256k1Service } from 'lacpass-key-manager'; +import { InternalServerError } from 'routing-controllers'; +import { ErrorsMessages } from '@constants/errorMessages'; +import { ISecp256k1 } from 'src/interfaces/key/key.interface'; @Service() export class KeyManagerService { - async createSecp256k1Key() { - return fetch(`${KEY_MANAGER_BASE_URL}${SECP256K1_KEY}`, { + public createSecp256k1Key: () => Promise; + private secp256k1Service: Secp256k1Service | null; + constructor() { + if (IS_INDEPENDENT_SERVICE !== 'true') { + this.createSecp256k1Key = this.createSecp256k1KeyByLib; + const S = require('lacpass-key-manager').Secp256k1DbService; + this.secp256k1Service = new S(); + } else { + this.secp256k1Service = null; + this.createSecp256k1Key = this.createKeyByExternalService; + } + } + async createSecp256k1KeyByLib(): Promise { + return (await this.secp256k1Service?.createKey()) as ISecp256k1; + } + async createKeyByExternalService(): Promise { + const result = await fetch(`${KEY_MANAGER_BASE_URL}${SECP256K1_KEY}`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }); + console.log('status', result.status); + if (result.status !== 200) { + console.log(await result.text()); + throw new InternalServerError(ErrorsMessages.CREATE_KEY_ERROR); + } + return (await result.json()) as ISecp256k1; } } diff --git a/yarn.lock b/yarn.lock index 312c88f..18fd53f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4763,6 +4763,42 @@ koa@^2.8.2: type-is "^1.6.16" vary "^1.1.2" +lacpass-key-manager@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/lacpass-key-manager/-/lacpass-key-manager-0.0.8.tgz#28ec13eab00a1affe0ea725e0826b6f02c8db333" + integrity sha512-62dd6sMijVox/ydbjoFvRHjwtr4rXZD6T1YWhqb7wIWloL5Jro/8YIsThD0J/v1RY1NiOiimPdJDrpj4LD6ejA== + dependencies: + aws-sdk "^2.1116.0" + bcrypt "^5.1.0" + body-parser "^1.20.0" + class-transformer "^0.5.1" + class-validator "^0.12.2" + class-validator-jsonschema "^2.2.0" + cors "^2.8.5" + dotenv "^16.0.3" + ethers "^6.3.0" + express "^4.17.3" + express-formidable "^1.2.0" + express-rate-limit "^6.3.0" + helmet "^5.0.2" + jsonwebtoken "^9.0.0" + morgan "^1.10.0" + multer "^1.4.4" + nodemailer "^6.7.3" + nodemailer-express-handlebars "^5.0.0" + nodemailer-sendgrid-transport "^0.2.0" + pg "^8.7.3" + redis "^3.1.2" + reflect-metadata "^0.1.13" + routing-controllers "^0.9.0" + routing-controllers-openapi "^3.1.0" + swagger-ui-express "^4.3.0" + ts-jest "^26.5.6" + tsconfig-paths "^3.14.1" + typedi "^0.10.0" + typeorm "^0.2.41" + typeorm-seeding "^1.6.1" + latest-version@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" From a8b8c867d709555025cd9692f80473d455a9354a Mon Sep 17 00:00:00 2001 From: eum602 Date: Sat, 22 Apr 2023 22:06:22 -0500 Subject: [PATCH 06/22] chore: add logger config --- ormconfig.ts | 10 +++++++++- package.json | 4 +++- src/config/LogConfig.ts | 14 ++++++++++++++ src/services/external/key-manager.service.ts | 4 ++++ yarn.lock | 10 ++++++++++ 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/config/LogConfig.ts diff --git a/ormconfig.ts b/ormconfig.ts index 17c7997..290f0d1 100644 --- a/ormconfig.ts +++ b/ormconfig.ts @@ -1,3 +1,4 @@ +import { log4TSProvider } from './src/config/LogConfig'; import { ConnectionOptions } from 'typeorm'; import { TYPEORM_HOST, @@ -13,7 +14,9 @@ import { IS_INDEPENDENT_SERVICE } from '@config'; -import { Secp256k1 } from 'lacpass-key-manager'; +import { Secp256k1, Secp256k1DbService } from 'lacpass-key-manager'; + +const log = log4TSProvider.getLogger('ormConfig'); const config: ConnectionOptions = { type: TYPEORM_TYPE as 'mysql' | 'postgres' | 'mongodb', @@ -42,7 +45,12 @@ const config: ConnectionOptions = { }; if (IS_INDEPENDENT_SERVICE !== 'true') { + log.info('Importing entities from external components'); config.entities?.push(Secp256k1); + // eslint-disable-next-line max-len + typeof Secp256k1DbService; // validates this service exist as of the key-manager version being installed +} else { + log.info('Initializing with local entities'); } export = config; diff --git a/package.json b/package.json index 5ded602..539f798 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,8 @@ "tsconfig-paths": "^3.14.1", "typedi": "^0.10.0", "typeorm": "^0.2.41", - "typeorm-seeding": "^1.6.1" + "typeorm-seeding": "^1.6.1", + "typescript-logging": "^2.1.0", + "typescript-logging-log4ts-style": "^2.1.0" } } diff --git a/src/config/LogConfig.ts b/src/config/LogConfig.ts new file mode 100644 index 0000000..64e04a4 --- /dev/null +++ b/src/config/LogConfig.ts @@ -0,0 +1,14 @@ +import { LogLevel } from 'typescript-logging'; +import { Log4TSProvider } from 'typescript-logging-log4ts-style'; + +export const log4TSProvider = Log4TSProvider.createProvider( + 'AwesomeLog4TSProvider', + { + level: LogLevel.Debug, + groups: [ + { + expression: new RegExp('.+') + } + ] + } +); diff --git a/src/services/external/key-manager.service.ts b/src/services/external/key-manager.service.ts index 24e6f47..f96a6e4 100644 --- a/src/services/external/key-manager.service.ts +++ b/src/services/external/key-manager.service.ts @@ -9,16 +9,20 @@ import { Secp256k1Service } from 'lacpass-key-manager'; import { InternalServerError } from 'routing-controllers'; import { ErrorsMessages } from '@constants/errorMessages'; import { ISecp256k1 } from 'src/interfaces/key/key.interface'; +import { log4TSProvider } from 'src/config/LogConfig'; @Service() export class KeyManagerService { public createSecp256k1Key: () => Promise; private secp256k1Service: Secp256k1Service | null; + log = log4TSProvider.getLogger('KeyManagerService'); constructor() { if (IS_INDEPENDENT_SERVICE !== 'true') { + this.log.info('Configuring key-manager library usage'); this.createSecp256k1Key = this.createSecp256k1KeyByLib; const S = require('lacpass-key-manager').Secp256k1DbService; this.secp256k1Service = new S(); } else { + this.log.info('Configuring key-manager external service connection'); this.secp256k1Service = null; this.createSecp256k1Key = this.createKeyByExternalService; } diff --git a/yarn.lock b/yarn.lock index 18fd53f..d19f609 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7218,6 +7218,16 @@ typeorm@^0.2.41: yargs "^17.0.1" zen-observable-ts "^1.0.0" +typescript-logging-log4ts-style@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/typescript-logging-log4ts-style/-/typescript-logging-log4ts-style-2.1.0.tgz#839e121f5f3b37f00f26a2a2b3db4ecb5d125089" + integrity sha512-yXvvi4MbDlvYel3c2/Z1kdf4PiiJ9rUe64iP+GOqrsSEwnCnWh78JLo7yYf7CNOEhaqZBAUa1P5GXoFxoGhTQQ== + +typescript-logging@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/typescript-logging/-/typescript-logging-2.1.0.tgz#7ecbed6274e9e301db54e993ac470e085bf76b52" + integrity sha512-3xi7Z0fslj8eQIEkiJSEvt5x8I4YDpwp7bdUcN8JDWXJ0RGD6RKkHhOCKgKu4KQU+4X0rm9zyxTK6TQst2hvWw== + typescript@^4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" From 1b5f3ef49024aaa9fcd24d3a63cbc4768a6b3cfb Mon Sep 17 00:00:00 2001 From: eum602 Date: Sun, 23 Apr 2023 13:52:59 -0500 Subject: [PATCH 07/22] chore: validate did registry and did domain name --- .example.env | 9 ++- ormconfig.ts | 4 +- src/config/LogConfig.ts | 19 +++-- src/config/index.ts | 76 +++++++++++++++++++- src/constants/networks/didRegistry.ts | 6 ++ src/controllers/did.controller.ts | 5 +- src/services/did.service.ts | 24 +++++-- src/services/external/key-manager.service.ts | 4 +- 8 files changed, 118 insertions(+), 29 deletions(-) create mode 100644 src/constants/networks/didRegistry.ts diff --git a/.example.env b/.example.env index 62e6d84..20a98b3 100644 --- a/.example.env +++ b/.example.env @@ -54,6 +54,11 @@ SENDGRID_API_KEY= EMAIL_TRANSPORTER = AWS # EXTERNAL SERVICES -# IS_INDEPENDENT_SERVICE = true # uncomment this only if the service communicates with the other components by way of external services +# IS_DEPENDENT_SERVICE = true # uncomment this only if the service communicates with the other components by way of external services KEY_MANAGER_BASE_URL=http://laccpass-key-manager/api/v1 -SECP256K1_KEY=/secp256k1 \ No newline at end of file +SECP256K1_KEY=/secp256k1 + +# Did Registry +CHAIN_ID = 0x9e55c +# DID_REGISTRY_ADDRESS = 0xBeB1df7Fb80CA88226aE1DaDa169639E950f3D79 # optional address, just in case you are willing to use another did registry +# DOMAIN_NAME = lacchain.id # optional param just in case you are willing to use another domain name \ No newline at end of file diff --git a/ormconfig.ts b/ormconfig.ts index 290f0d1..0b389ff 100644 --- a/ormconfig.ts +++ b/ormconfig.ts @@ -11,7 +11,7 @@ import { TYPEORM_MIGRATIONS_RUN, PRODUCTION_ENV, TYPEORM_TYPE, - IS_INDEPENDENT_SERVICE + IS_DEPENDENT_SERVICE } from '@config'; import { Secp256k1, Secp256k1DbService } from 'lacpass-key-manager'; @@ -44,7 +44,7 @@ const config: ConnectionOptions = { } }; -if (IS_INDEPENDENT_SERVICE !== 'true') { +if (IS_DEPENDENT_SERVICE !== 'true') { log.info('Importing entities from external components'); config.entities?.push(Secp256k1); // eslint-disable-next-line max-len diff --git a/src/config/LogConfig.ts b/src/config/LogConfig.ts index 64e04a4..b2f66d0 100644 --- a/src/config/LogConfig.ts +++ b/src/config/LogConfig.ts @@ -1,14 +1,11 @@ import { LogLevel } from 'typescript-logging'; import { Log4TSProvider } from 'typescript-logging-log4ts-style'; -export const log4TSProvider = Log4TSProvider.createProvider( - 'AwesomeLog4TSProvider', - { - level: LogLevel.Debug, - groups: [ - { - expression: new RegExp('.+') - } - ] - } -); +export const log4TSProvider = Log4TSProvider.createProvider('Log4Provider', { + level: LogLevel.Debug, + groups: [ + { + expression: new RegExp('.+') + } + ] +}); diff --git a/src/config/index.ts b/src/config/index.ts index aed7a04..41c5452 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,18 +1,92 @@ +import { + DEFAULT_DOMAIN_NAME, + DEFAULT_REGISTRY, + DOMAIN_NAMES, + REGISTRY +} from '@constants/networks/didRegistry'; import { config } from 'dotenv'; +import { ethers } from 'ethers'; +import { log4TSProvider } from './LogConfig'; config({ path: `.env.${process.env.ENV || 'dev'}` }); +const log = log4TSProvider.getLogger('config'); + // If .env wasn't provided then exit if (!process.env.PORT) { console.error('==> Please check your .env'); process.exit(1); } +if (!process.env.CHAIN_ID) { + console.error('==> Please set CHAIN_ID in your .env'); + process.exit(1); +} + +export const CHAIN_ID = process.env.CHAIN_ID; + export const PRODUCTION_ENV = process.env.ENV === 'prod'; export const DEV_ENV = process.env.ENV === 'dev'; export const TESTING_ENV = process.env.ENV === 'test'; export const CI_ENV = process.env.ENV === 'ci'; export const JWT_SECRET_DEFAULT = 'some-secret-string-default'; +const resolveDidRegistryAddress = (): string => { + const didRegistryAddress = process.env.DID_REGISTRY_ADDRESS; + if (didRegistryAddress) { + if (!ethers.isAddress(didRegistryAddress)) { + log.error( + 'Specified DID_REGISTRY_ADDRESS', + DID_REGISTRY_ADDRESS, + 'is not a valid address ... exiting' + ); + process.exit(1); // exiting since this is a critical error + } + if ( + didRegistryAddress && + !REGISTRY.get(CHAIN_ID)?.find(el => el === didRegistryAddress) + ) { + log.info('Unknown specified did registry address ', didRegistryAddress); + } + log.info('Returning custom did registry address', didRegistryAddress); + return didRegistryAddress; + } + const wellKnownRegistryAddress = DEFAULT_REGISTRY.get(CHAIN_ID); + if (!wellKnownRegistryAddress) { + log.error('Could not find well-known registry address for chain', CHAIN_ID); + process.exit(1); // exiting since this is a critical error + } + log.info('Returning default registry address', wellKnownRegistryAddress); + return wellKnownRegistryAddress; +}; + +const resolveDidDomainName = (): string => { + const domainName = process.env.DOMAIN_NAME; + if (domainName) { + const resolvedHostnames = DOMAIN_NAMES; + if (!resolvedHostnames?.find(name => name === domainName)) { + log.error( + 'Specified domain ', + domainName, + ' does not match any well known domain for chain Id', + CHAIN_ID + ); + process.exit(1); + } + log.info('Returning domain name', domainName); + return domainName; + } + const defaultDomainName = DEFAULT_DOMAIN_NAME; + if (!defaultDomainName) { + log.error('Could not find default domain name for chain', CHAIN_ID); + process.exit(1); // exiting since this is a critical error + } + log.info('Returning default domain name', defaultDomainName); + return defaultDomainName; +}; + +export const DID_REGISTRY_ADDRESS = resolveDidRegistryAddress(); +export const DOMAIN_NAME = resolveDidDomainName(); + export const { ENV, PORT, @@ -42,5 +116,5 @@ export const { SENDGRID_API_KEY, KEY_MANAGER_BASE_URL, SECP256K1_KEY, - IS_INDEPENDENT_SERVICE + IS_DEPENDENT_SERVICE } = process.env; diff --git a/src/constants/networks/didRegistry.ts b/src/constants/networks/didRegistry.ts new file mode 100644 index 0000000..e20117a --- /dev/null +++ b/src/constants/networks/didRegistry.ts @@ -0,0 +1,6 @@ +export const REGISTRY: Map = new Map(); +REGISTRY.set('0x9e55c', ['0xBeB1df7Fb80CA88226aE1DaDa169639E950f3D79']); +export const DEFAULT_REGISTRY: Map = new Map(); +DEFAULT_REGISTRY.set('0x9e55c', '0xBeB1df7Fb80CA88226aE1DaDa169639E950f3D79'); +export const DOMAIN_NAMES: string[] = ['lacchain.id', 'another.lacchain.id']; +export const DEFAULT_DOMAIN_NAME = 'lacchain.id'; diff --git a/src/controllers/did.controller.ts b/src/controllers/did.controller.ts index 10a1822..cfeb472 100644 --- a/src/controllers/did.controller.ts +++ b/src/controllers/did.controller.ts @@ -1,8 +1,6 @@ import { JsonController, Post, BadRequestError } from 'routing-controllers'; import { Service } from 'typedi'; -import { EntityMapper } from '@clients/mapper/entityMapper.service'; import { DidService } from '@services/did.service'; -import { Did } from '@entities/did.entity'; import { ErrorsMessages } from '@constants/errorMessages'; @JsonController('/did') @@ -13,8 +11,7 @@ export class DidController { @Post('/web-lac') async createDidWebLac(): Promise { try { - const did = EntityMapper.mapTo(Did, {}); - return this.didService.createDidWebLac(did); + return this.didService.createDidWebLac(); } catch (error: any) { throw new BadRequestError( error.detail ?? error.message ?? ErrorsMessages.INTERNAL_SERVER_ERROR diff --git a/src/services/did.service.ts b/src/services/did.service.ts index 8ec14de..fb5c732 100644 --- a/src/services/did.service.ts +++ b/src/services/did.service.ts @@ -2,6 +2,9 @@ import { Service } from 'typedi'; import { getRepository } from 'typeorm'; import { Did } from '@entities/did.entity'; import { KeyManagerService } from './external/key-manager.service'; +import { EntityMapper } from '@clients/mapper/entityMapper.service'; +import { CHAIN_ID, DID_REGISTRY_ADDRESS, DOMAIN_NAME } from '@config'; +import { log4TSProvider } from 'src/config/LogConfig'; @Service() export class DidService { @@ -13,22 +16,29 @@ export class DidService { private readonly didEncodingVersion = '0001'; // constant for encoding // eslint-disable-next-line max-len - private readonly didType = '0001'; // lac -> make a table for the different kinds of supported did types - private readonly chainId = '0x9e55c'; - private readonly lacchainHostname = 'lacchain.id'; - private readonly didLacWebIdentifier: string; + private readonly didType = '0001'; // constant + private readonly domainName: string; + + private readonly chainId: string; private readonly didRegistryAddress: string; + + private readonly didLacWebIdentifier: string; + + log = log4TSProvider.getLogger('didService'); constructor(private keyManagerService: KeyManagerService) { - this.didLacWebIdentifier = `did:web:${this.lacchainHostname}:`; - this.didRegistryAddress = '0xBeB1df7Fb80CA88226aE1DaDa169639E950f3D79'; + this.chainId = CHAIN_ID; + this.didRegistryAddress = DID_REGISTRY_ADDRESS; + this.domainName = DOMAIN_NAME; + this.didLacWebIdentifier = `did:web:${this.domainName}:`; } show(id: string) { return this.didRepository.findOne(id); } - async createDidWebLac(did: Did) { + async createDidWebLac() { const key = await this.keyManagerService.createSecp256k1Key(); + const did = EntityMapper.mapTo(Did, {}); did.keyId = key.keyId; did.did = this.didLacWebIdentifier + diff --git a/src/services/external/key-manager.service.ts b/src/services/external/key-manager.service.ts index f96a6e4..e360c79 100644 --- a/src/services/external/key-manager.service.ts +++ b/src/services/external/key-manager.service.ts @@ -1,7 +1,7 @@ import { Service } from 'typedi'; import fetch from 'node-fetch'; import { - IS_INDEPENDENT_SERVICE, + IS_DEPENDENT_SERVICE, KEY_MANAGER_BASE_URL, SECP256K1_KEY } from '@config'; @@ -16,7 +16,7 @@ export class KeyManagerService { private secp256k1Service: Secp256k1Service | null; log = log4TSProvider.getLogger('KeyManagerService'); constructor() { - if (IS_INDEPENDENT_SERVICE !== 'true') { + if (IS_DEPENDENT_SERVICE !== 'true') { this.log.info('Configuring key-manager library usage'); this.createSecp256k1Key = this.createSecp256k1KeyByLib; const S = require('lacpass-key-manager').Secp256k1DbService; From 68f63335430f809afab5fb6f7a49cfa20dd46e59 Mon Sep 17 00:00:00 2001 From: eum602 Date: Sun, 23 Apr 2023 17:23:20 -0500 Subject: [PATCH 08/22] chore: add docker-compose config for production created images --- .example.env | 2 +- docker-compose-dev.yml | 58 ++++++++++++++++++++ docker-compose.yml | 5 +- docs/tech/configuration.md | 21 ++++++- ormconfig.ts | 4 +- prod-paths.js | 5 ++ src/config/LogConfig.ts | 11 ---- src/config/index.ts | 13 ++++- src/services/did.service.ts | 8 ++- src/services/external/key-manager.service.ts | 4 +- 10 files changed, 107 insertions(+), 24 deletions(-) create mode 100644 docker-compose-dev.yml delete mode 100644 src/config/LogConfig.ts diff --git a/.example.env b/.example.env index 20a98b3..4880def 100644 --- a/.example.env +++ b/.example.env @@ -60,5 +60,5 @@ SECP256K1_KEY=/secp256k1 # Did Registry CHAIN_ID = 0x9e55c -# DID_REGISTRY_ADDRESS = 0xBeB1df7Fb80CA88226aE1DaDa169639E950f3D79 # optional address, just in case you are willing to use another did registry +# DID_REGISTRY_ADDRESS = 0x00f23cda3cbec27ae630894dd84e88033676d136 # optional address, just in case you are willing to use another did registry # DOMAIN_NAME = lacchain.id # optional param just in case you are willing to use another domain name \ No newline at end of file diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml new file mode 100644 index 0000000..dd48a55 --- /dev/null +++ b/docker-compose-dev.yml @@ -0,0 +1,58 @@ +version: '3' +services: + postgres: + image: postgres:latest + ports: + - '${EXPOSED_CONTAINER_TYPEORM_PORT}:${TYPEORM_PORT}' + environment: + POSTGRES_PASSWORD: ${TYPEORM_PASSWORD} + volumes: + - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql + networks: + - backend + redis: + image: redis:latest + command: redis-server --requirepass ${REDIS_PASSWORD} + ports: + - '${EXPOSED_CONTAINER_REDIS_PORT}:${REDIS_PORT}' + networks: + - backend + node-api-base: + build: + context: ./ + dockerfile: Dockerfile.dev + environment: + PORT: "${PORT}" + JWT_SECRET: "${JWT_SECRET}" + TYPEORM_TYPE: postgres + TYPEORM_HOST: postgres + TYPEORM_USERNAME: postgres + TYPEORM_PASSWORD: postgres + TYPEORM_DATABASE: "${TYPEORM_DATABASE}" + TYPEORM_PORT: "${TYPEORM_PORT}" + TYPEORM_SYNCHRONIZE: "true" + TYPEORM_LOGGING: "true" + TYPEORM_MIGRATIONS_RUN: "${TYPEORM_MIGRATIONS_RUN}" + ACCESS_TOKEN_LIFE: "${ACCESS_TOKEN_LIFE}" + RATE_LIMIT_WINDOW: "${RATE_LIMIT_WINDOW}" + RATE_LIMIT_MAX_REQUESTS: "${RATE_LIMIT_MAX_REQUESTS}" + ports: + - '${EXPOSED_CONTAINER_SERVER_PORT}:${PORT}' + restart: on-failure + container_name: laccpass-identity-manager + depends_on: + - postgres + - redis + links: + - postgres + - redis + volumes: + - "./src:/app/src" + - /src/node_modules + - ".env.dev:/app/.env.dev" + networks: + - backend +networks: + backend: + name: backend + external: true \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 8e2b9fe..26e7934 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,7 +20,7 @@ services: node-api-base: build: context: ./ - dockerfile: Dockerfile.dev + dockerfile: Dockerfile.prod environment: PORT: "${PORT}" JWT_SECRET: "${JWT_SECRET}" @@ -47,8 +47,7 @@ services: - postgres - redis volumes: - - "./src:/app/src" - - /src/node_modules + - ".env.prod:/app/.env.prod" networks: - backend networks: diff --git a/docs/tech/configuration.md b/docs/tech/configuration.md index 3174a4f..6f9a615 100644 --- a/docs/tech/configuration.md +++ b/docs/tech/configuration.md @@ -69,11 +69,28 @@ docker network create backend ``` ``` -docker-compose --env-file .env.dev build +docker-compose -f docker-compose-dev.yml --env-file .env.dev build ``` ``` -docker-compose --env-file .env.dev up +docker-compose -f docker-compose-dev.yml --env-file .env.dev up +``` + + +### Testing Docker production images with docker-compose + +The following commands will build and run all you need to start working on the base, without any other installation requirements. Important: if you already have postgres running locally, you'll need to kill the service before run `docker-compose up`. + +``` +docker network create backend +``` + +``` +docker-compose -f docker-compose.yml --env-file .env.prod build +``` + +``` +docker-compose -f docker-compose.yml --env-file .env.prod up ``` ### Deployment with Docker (only for production) diff --git a/ormconfig.ts b/ormconfig.ts index 0b389ff..1d3e98a 100644 --- a/ormconfig.ts +++ b/ormconfig.ts @@ -1,4 +1,3 @@ -import { log4TSProvider } from './src/config/LogConfig'; import { ConnectionOptions } from 'typeorm'; import { TYPEORM_HOST, @@ -11,7 +10,8 @@ import { TYPEORM_MIGRATIONS_RUN, PRODUCTION_ENV, TYPEORM_TYPE, - IS_DEPENDENT_SERVICE + IS_DEPENDENT_SERVICE, + log4TSProvider } from '@config'; import { Secp256k1, Secp256k1DbService } from 'lacpass-key-manager'; diff --git a/prod-paths.js b/prod-paths.js index 849483b..9da8f28 100644 --- a/prod-paths.js +++ b/prod-paths.js @@ -8,6 +8,11 @@ for (const path in paths) { paths[path][0] = paths[path][0] .replace('src', 'dist/src') .replace('.ts', '.js'); + if (paths[path][0] === 'ormconfig') { + paths[path][0] = paths[path][0] + .replace('ormconfig', 'dist/ormconfig') + .replace('.ts', '.js'); + } } tsConfigPaths.register({ baseUrl, paths }); diff --git a/src/config/LogConfig.ts b/src/config/LogConfig.ts deleted file mode 100644 index b2f66d0..0000000 --- a/src/config/LogConfig.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { LogLevel } from 'typescript-logging'; -import { Log4TSProvider } from 'typescript-logging-log4ts-style'; - -export const log4TSProvider = Log4TSProvider.createProvider('Log4Provider', { - level: LogLevel.Debug, - groups: [ - { - expression: new RegExp('.+') - } - ] -}); diff --git a/src/config/index.ts b/src/config/index.ts index 41c5452..589e635 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -6,9 +6,20 @@ import { } from '@constants/networks/didRegistry'; import { config } from 'dotenv'; import { ethers } from 'ethers'; -import { log4TSProvider } from './LogConfig'; +import { LogLevel } from 'typescript-logging'; +import { Log4TSProvider } from 'typescript-logging-log4ts-style'; + config({ path: `.env.${process.env.ENV || 'dev'}` }); +export const log4TSProvider = Log4TSProvider.createProvider('Log4Provider', { + level: LogLevel.Debug, + groups: [ + { + expression: new RegExp('.+') + } + ] +}); + const log = log4TSProvider.getLogger('config'); // If .env wasn't provided then exit diff --git a/src/services/did.service.ts b/src/services/did.service.ts index fb5c732..a9c1e64 100644 --- a/src/services/did.service.ts +++ b/src/services/did.service.ts @@ -3,8 +3,12 @@ import { getRepository } from 'typeorm'; import { Did } from '@entities/did.entity'; import { KeyManagerService } from './external/key-manager.service'; import { EntityMapper } from '@clients/mapper/entityMapper.service'; -import { CHAIN_ID, DID_REGISTRY_ADDRESS, DOMAIN_NAME } from '@config'; -import { log4TSProvider } from 'src/config/LogConfig'; +import { + CHAIN_ID, + DID_REGISTRY_ADDRESS, + DOMAIN_NAME, + log4TSProvider +} from '@config'; @Service() export class DidService { diff --git a/src/services/external/key-manager.service.ts b/src/services/external/key-manager.service.ts index e360c79..8a8207d 100644 --- a/src/services/external/key-manager.service.ts +++ b/src/services/external/key-manager.service.ts @@ -3,13 +3,13 @@ import fetch from 'node-fetch'; import { IS_DEPENDENT_SERVICE, KEY_MANAGER_BASE_URL, - SECP256K1_KEY + SECP256K1_KEY, + log4TSProvider } from '@config'; import { Secp256k1Service } from 'lacpass-key-manager'; import { InternalServerError } from 'routing-controllers'; import { ErrorsMessages } from '@constants/errorMessages'; import { ISecp256k1 } from 'src/interfaces/key/key.interface'; -import { log4TSProvider } from 'src/config/LogConfig'; @Service() export class KeyManagerService { public createSecp256k1Key: () => Promise; From 89d194b0c21ee70c4febeb1e74e5aee477b74a01 Mon Sep 17 00:00:00 2001 From: eum602 Date: Sun, 23 Apr 2023 17:50:08 -0500 Subject: [PATCH 09/22] chore: config volumes --- docker-compose.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 26e7934..4ecf13e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,11 @@ services: POSTGRES_PASSWORD: ${TYPEORM_PASSWORD} volumes: - ./docker_postgres_init.sql:/docker-entrypoint-initdb.d/docker_postgres_init.sql + - type: bind + source: ./pg-prod-db + target: /var/lib/postgresql/data + volume: + nocopy: true networks: - backend redis: @@ -53,4 +58,4 @@ services: networks: backend: name: backend - external: true \ No newline at end of file + external: true From 5f1397736407e1b18771b4f69dd67e555c810921 Mon Sep 17 00:00:00 2001 From: eum602 Date: Sun, 23 Apr 2023 18:01:32 -0500 Subject: [PATCH 10/22] docs: volume for db --- .example.env | 2 +- docs/tech/configuration.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.example.env b/.example.env index 4880def..5218171 100644 --- a/.example.env +++ b/.example.env @@ -13,7 +13,7 @@ TYPEORM_TYPE = postgres TYPEORM_HOST = localhost TYPEORM_USERNAME = postgres TYPEORM_PASSWORD = postgres -TYPEORM_DATABASE = laccpass_identity_development +TYPEORM_DATABASE = laccpass_identity TYPEORM_PORT = 5432 TYPEORM_SYNCHRONIZE = false TYPEORM_LOGGING = true diff --git a/docs/tech/configuration.md b/docs/tech/configuration.md index 6f9a615..96afcb5 100644 --- a/docs/tech/configuration.md +++ b/docs/tech/configuration.md @@ -85,6 +85,10 @@ The following commands will build and run all you need to start working on the b docker network create backend ``` +``` +mkdir pg-prod-db +``` + ``` docker-compose -f docker-compose.yml --env-file .env.prod build ``` From 0c0bdd4664d724a48e8c3f8936d423972bd27fcb Mon Sep 17 00:00:00 2001 From: eum602 Date: Mon, 24 Apr 2023 09:42:24 -0500 Subject: [PATCH 11/22] chore: configure project to export some classes in a packaged library --- package.json | 7 +++++-- src/controllers/did.controller.ts | 10 +++++----- src/index.ts | 4 ++++ src/services/did.service.ts | 5 +++-- src/services/interfaces/did.service.ts | 6 ++++++ tsconfig.json | 6 +++++- 6 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 src/index.ts create mode 100644 src/services/interfaces/did.service.ts diff --git a/package.json b/package.json index 539f798..82b6e99 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,10 @@ { - "name": "identity", - "version": "0.0.1", + "name": "lacpass-identity", + "version": "0.0.2", "description": "Rest api for laccpass identity manager", + "main": "dist/src/index.js", + "types": "dist/src/index.d.ts", + "files": ["/dist"], "license": "MIT", "scripts": { "dev": "yarn && nodemon --watch 'src/**/*.ts' --exec 'ENV=dev ts-node -r tsconfig-paths/register' src/server.ts", diff --git a/src/controllers/did.controller.ts b/src/controllers/did.controller.ts index cfeb472..db73f46 100644 --- a/src/controllers/did.controller.ts +++ b/src/controllers/did.controller.ts @@ -1,17 +1,17 @@ import { JsonController, Post, BadRequestError } from 'routing-controllers'; import { Service } from 'typedi'; -import { DidService } from '@services/did.service'; +import { DidServiceWebLac } from '@services/did.service'; import { ErrorsMessages } from '@constants/errorMessages'; -@JsonController('/did') +@JsonController('/did-web-lac') @Service() export class DidController { - constructor(private readonly didService: DidService) {} + constructor(private readonly didServiceWebLac: DidServiceWebLac) {} - @Post('/web-lac') + @Post() async createDidWebLac(): Promise { try { - return this.didService.createDidWebLac(); + return this.didServiceWebLac.createDid(); } catch (error: any) { throw new BadRequestError( error.detail ?? error.message ?? ErrorsMessages.INTERNAL_SERVER_ERROR diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..e49614f --- /dev/null +++ b/src/index.ts @@ -0,0 +1,4 @@ +export { DidServiceWebLac } from './services/did.service'; +export { DidService, did as DidType } from './services/interfaces/did.service'; +export { Did as DidEntity } from './entities/did.entity'; +export { Secp256k1 as Secp256k1Entity } from 'lacpass-key-manager'; diff --git a/src/services/did.service.ts b/src/services/did.service.ts index a9c1e64..bc82c78 100644 --- a/src/services/did.service.ts +++ b/src/services/did.service.ts @@ -9,9 +9,10 @@ import { DOMAIN_NAME, log4TSProvider } from '@config'; +import { DidService } from './interfaces/did.service'; @Service() -export class DidService { +export class DidServiceWebLac implements DidService { private readonly didRepository = getRepository(Did); private readonly base58 = require('base-x')( '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' @@ -40,7 +41,7 @@ export class DidService { return this.didRepository.findOne(id); } - async createDidWebLac() { + async createDid() { const key = await this.keyManagerService.createSecp256k1Key(); const did = EntityMapper.mapTo(Did, {}); did.keyId = key.keyId; diff --git a/src/services/interfaces/did.service.ts b/src/services/interfaces/did.service.ts new file mode 100644 index 0000000..61b319f --- /dev/null +++ b/src/services/interfaces/did.service.ts @@ -0,0 +1,6 @@ +export type did = { + did: string; +}; +export interface DidService { + createDid(): Promise; +} diff --git a/tsconfig.json b/tsconfig.json index b60405a..91bf89b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "target": "es6", + "declaration": true, "module": "commonjs", "sourceMap": true, "outDir": "dist", @@ -35,5 +36,8 @@ "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, - "exclude": ["node_modules", "dist"] + "exclude": ["node_modules", "dist"], + "include": [ + "src/**/*" + ] } From f860342f6e6b2faf05cc31f0d703710bc3e018a8 Mon Sep 17 00:00:00 2001 From: eum602 Date: Mon, 24 Apr 2023 15:51:57 -0500 Subject: [PATCH 12/22] fix: paths --- package.json | 2 +- src/config/index.ts | 19 +++++++++++-------- src/services/did.service.ts | 22 ++++++++++++---------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 82b6e99..d5dcce7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lacpass-identity", - "version": "0.0.2", + "version": "0.0.8", "description": "Rest api for laccpass identity manager", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", diff --git a/src/config/index.ts b/src/config/index.ts index 589e635..67cf78b 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -3,7 +3,7 @@ import { DEFAULT_REGISTRY, DOMAIN_NAMES, REGISTRY -} from '@constants/networks/didRegistry'; +} from '../constants/networks/didRegistry'; import { config } from 'dotenv'; import { ethers } from 'ethers'; import { LogLevel } from 'typescript-logging'; @@ -28,12 +28,15 @@ if (!process.env.PORT) { process.exit(1); } -if (!process.env.CHAIN_ID) { - console.error('==> Please set CHAIN_ID in your .env'); - process.exit(1); -} +export const getChainId = (): string => { + if (!process.env.CHAIN_ID) { + console.error('==> Please set CHAIN_ID in your .env'); + process.exit(1); + } + return process.env.CHAIN_ID; +}; -export const CHAIN_ID = process.env.CHAIN_ID; +export const CHAIN_ID = getChainId(); export const PRODUCTION_ENV = process.env.ENV === 'prod'; export const DEV_ENV = process.env.ENV === 'dev'; @@ -41,7 +44,7 @@ export const TESTING_ENV = process.env.ENV === 'test'; export const CI_ENV = process.env.ENV === 'ci'; export const JWT_SECRET_DEFAULT = 'some-secret-string-default'; -const resolveDidRegistryAddress = (): string => { +export const resolveDidRegistryAddress = (): string => { const didRegistryAddress = process.env.DID_REGISTRY_ADDRESS; if (didRegistryAddress) { if (!ethers.isAddress(didRegistryAddress)) { @@ -70,7 +73,7 @@ const resolveDidRegistryAddress = (): string => { return wellKnownRegistryAddress; }; -const resolveDidDomainName = (): string => { +export const resolveDidDomainName = (): string => { const domainName = process.env.DOMAIN_NAME; if (domainName) { const resolvedHostnames = DOMAIN_NAMES; diff --git a/src/services/did.service.ts b/src/services/did.service.ts index bc82c78..ec3e8b9 100644 --- a/src/services/did.service.ts +++ b/src/services/did.service.ts @@ -1,14 +1,14 @@ import { Service } from 'typedi'; import { getRepository } from 'typeorm'; -import { Did } from '@entities/did.entity'; +import { Did } from '../entities/did.entity'; import { KeyManagerService } from './external/key-manager.service'; import { EntityMapper } from '@clients/mapper/entityMapper.service'; import { - CHAIN_ID, - DID_REGISTRY_ADDRESS, - DOMAIN_NAME, - log4TSProvider -} from '@config'; + getChainId, + log4TSProvider, + resolveDidDomainName, + resolveDidRegistryAddress +} from '../config'; import { DidService } from './interfaces/did.service'; @Service() @@ -30,10 +30,12 @@ export class DidServiceWebLac implements DidService { private readonly didLacWebIdentifier: string; log = log4TSProvider.getLogger('didService'); - constructor(private keyManagerService: KeyManagerService) { - this.chainId = CHAIN_ID; - this.didRegistryAddress = DID_REGISTRY_ADDRESS; - this.domainName = DOMAIN_NAME; + private keyManagerService; + constructor() { + this.keyManagerService = new KeyManagerService(); + this.chainId = getChainId(); + this.didRegistryAddress = resolveDidRegistryAddress(); + this.domainName = resolveDidDomainName(); this.didLacWebIdentifier = `did:web:${this.domainName}:`; } From a82ef1ec70b4d0f4dd897f6f1db62821f2f0af2d Mon Sep 17 00:00:00 2001 From: eum602 Date: Thu, 27 Apr 2023 09:04:49 -0500 Subject: [PATCH 13/22] chore: downgrade ether version --- package.json | 7 +- src/config/index.ts | 4 +- yarn.lock | 469 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 475 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index d5dcce7..7af193e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "Rest api for laccpass identity manager", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", - "files": ["/dist"], + "files": [ + "/dist" + ], "license": "MIT", "scripts": { "dev": "yarn && nodemon --watch 'src/**/*.ts' --exec 'ENV=dev ts-node -r tsconfig-paths/register' src/server.ts", @@ -66,6 +68,7 @@ "typescript": "^4.9.5" }, "dependencies": { + "@lacchain/gas-model-provider": "^1.0.1", "aws-sdk": "^2.1116.0", "base-x": "^4.0.0", "bcrypt": "^5.1.0", @@ -75,7 +78,7 @@ "class-validator-jsonschema": "^2.2.0", "cors": "^2.8.5", "dotenv": "^16.0.3", - "ethers": "^6.3.0", + "ethers": "^5.6.5", "express": "^4.17.3", "express-formidable": "^1.2.0", "express-rate-limit": "^6.3.0", diff --git a/src/config/index.ts b/src/config/index.ts index 67cf78b..9654d14 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -3,7 +3,7 @@ import { DEFAULT_REGISTRY, DOMAIN_NAMES, REGISTRY -} from '../constants/networks/didRegistry'; +} from '../constants/did-web/lac/didRegistryAddresses'; import { config } from 'dotenv'; import { ethers } from 'ethers'; import { LogLevel } from 'typescript-logging'; @@ -47,7 +47,7 @@ export const JWT_SECRET_DEFAULT = 'some-secret-string-default'; export const resolveDidRegistryAddress = (): string => { const didRegistryAddress = process.env.DID_REGISTRY_ADDRESS; if (didRegistryAddress) { - if (!ethers.isAddress(didRegistryAddress)) { + if (!ethers.utils.isAddress(didRegistryAddress)) { log.error( 'Specified DID_REGISTRY_ADDRESS', DID_REGISTRY_ADDRESS, diff --git a/yarn.lock b/yarn.lock index d19f609..d30d70b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -323,6 +323,348 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" + integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.6.0", "@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/contracts@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" + integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + +"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" + integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" + integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" + integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + +"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/providers@5.7.2": + version "5.7.2" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" + integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + bech32 "1.1.4" + ws "7.4.6" + +"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" + integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" + integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + hash.js "1.1.7" + +"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/solidity@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" + integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.6.0", "@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/units@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" + integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/wallet@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" + integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/json-wallets" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" + integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@humanwhocodes/config-array@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" @@ -524,6 +866,15 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@lacchain/gas-model-provider@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@lacchain/gas-model-provider/-/gas-model-provider-1.0.1.tgz#f9ba0daa23a10e9566adfc89996797b79e9b4abe" + integrity sha512-/KwT+fY0h3m8fCH/SeQs5ODhxjEg38X1TZxg8MMeNMO9v7WPYLsilPYCA6nsbQDL2r0K+7yymYE27fPc2FdSbg== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + ethers "^5.6.0" + "@mapbox/node-pre-gyp@^1.0.10": version "1.0.10" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" @@ -1085,6 +1436,11 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== + aes-js@4.0.0-beta.3: version "4.0.0-beta.3" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.3.tgz#da2253f0ff03a0b3a9e445c8cbdf78e7fda7d48c" @@ -1530,11 +1886,26 @@ bcrypt@^5.1.0: "@mapbox/node-pre-gyp" "^1.0.10" node-addon-api "^5.0.0" +bech32@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + binary-extensions@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== +bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + body-parser@1.19.2: version "1.19.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" @@ -1626,6 +1997,11 @@ braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" @@ -2560,6 +2936,19 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +elliptic@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emittery@^0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" @@ -2826,6 +3215,42 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +ethers@^5.6.0, ethers@^5.6.5: + version "5.7.2" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" + integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== + dependencies: + "@ethersproject/abi" "5.7.0" + "@ethersproject/abstract-provider" "5.7.0" + "@ethersproject/abstract-signer" "5.7.0" + "@ethersproject/address" "5.7.0" + "@ethersproject/base64" "5.7.0" + "@ethersproject/basex" "5.7.0" + "@ethersproject/bignumber" "5.7.0" + "@ethersproject/bytes" "5.7.0" + "@ethersproject/constants" "5.7.0" + "@ethersproject/contracts" "5.7.0" + "@ethersproject/hash" "5.7.0" + "@ethersproject/hdnode" "5.7.0" + "@ethersproject/json-wallets" "5.7.0" + "@ethersproject/keccak256" "5.7.0" + "@ethersproject/logger" "5.7.0" + "@ethersproject/networks" "5.7.1" + "@ethersproject/pbkdf2" "5.7.0" + "@ethersproject/properties" "5.7.0" + "@ethersproject/providers" "5.7.2" + "@ethersproject/random" "5.7.0" + "@ethersproject/rlp" "5.7.0" + "@ethersproject/sha2" "5.7.0" + "@ethersproject/signing-key" "5.7.0" + "@ethersproject/solidity" "5.7.0" + "@ethersproject/strings" "5.7.0" + "@ethersproject/transactions" "5.7.0" + "@ethersproject/units" "5.7.0" + "@ethersproject/wallet" "5.7.0" + "@ethersproject/web" "5.7.1" + "@ethersproject/wordlists" "5.7.0" + ethers@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.3.0.tgz#c61efaafa2bd9a4d9f0c799d932ef3b5cd4bd37d" @@ -3514,6 +3939,14 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + helmet@*, helmet@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/helmet/-/helmet-5.0.2.tgz#3264ec6bab96c82deaf65e3403c369424cb2366c" @@ -3529,6 +3962,15 @@ highlight.js@^10.7.1: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + hosted-git-info@^2.1.4: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" @@ -3721,7 +4163,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4458,6 +4900,11 @@ jmespath@0.16.0: resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -5050,6 +5497,16 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -6334,6 +6791,11 @@ saxes@^5.0.0: dependencies: xmlchars "^2.2.0" +scrypt-js@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" @@ -7573,6 +8035,11 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" +ws@7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + ws@8.5.0: version "8.5.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" From 01519f3a51695704b753068562cf92290d4bbcac Mon Sep 17 00:00:00 2001 From: eum602 Date: Thu, 27 Apr 2023 11:57:52 -0500 Subject: [PATCH 14/22] chore: add config to add lacchain connection --- .example.env | 9 +- src/config/index.ts | 22 +- .../lac/didRegistryAddresses.ts} | 4 +- .../did-web/lac/lacDidRegistryAbi.ts | 425 ++++++++++++++++++ src/constants/errorMessages.ts | 3 +- 5 files changed, 457 insertions(+), 6 deletions(-) rename src/constants/{networks/didRegistry.ts => did-web/lac/didRegistryAddresses.ts} (65%) create mode 100644 src/constants/did-web/lac/lacDidRegistryAbi.ts diff --git a/.example.env b/.example.env index 5218171..788d04b 100644 --- a/.example.env +++ b/.example.env @@ -61,4 +61,11 @@ SECP256K1_KEY=/secp256k1 # Did Registry CHAIN_ID = 0x9e55c # DID_REGISTRY_ADDRESS = 0x00f23cda3cbec27ae630894dd84e88033676d136 # optional address, just in case you are willing to use another did registry -# DOMAIN_NAME = lacchain.id # optional param just in case you are willing to use another domain name \ No newline at end of file +# DOMAIN_NAME = lacchain.id # optional param just in case you are willing to use another domain name + +# Blockchain Connection +# for open protestnet: 'http://35.185.112.219' +# for mainnet: TBD +NODE_RPC_URL = http://35.185.112.219 +#Node Address depends on the lacchain node you are connecting with +NODE_ADDRESS = 0xad730de8c4bfc3d845f7ce851bcf2ea17c049585 \ No newline at end of file diff --git a/src/config/index.ts b/src/config/index.ts index 9654d14..9cd99b6 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -36,6 +36,22 @@ export const getChainId = (): string => { return process.env.CHAIN_ID; }; +export const getRpcUrl = (): string => { + if (!process.env.NODE_RPC_URL) { + console.error('==> Please set RPC_URL in your .env'); + process.exit(1); + } + return process.env.NODE_RPC_URL; +}; + +export const getNodeAddress = (): string => { + if (!process.env.NODE_ADDRESS) { + console.error('==> Please set NODE_ADDRESS in your .env'); + process.exit(1); + } + return process.env.NODE_ADDRESS; +}; + export const CHAIN_ID = getChainId(); export const PRODUCTION_ENV = process.env.ENV === 'prod'; @@ -44,8 +60,10 @@ export const TESTING_ENV = process.env.ENV === 'test'; export const CI_ENV = process.env.ENV === 'ci'; export const JWT_SECRET_DEFAULT = 'some-secret-string-default'; -export const resolveDidRegistryAddress = (): string => { - const didRegistryAddress = process.env.DID_REGISTRY_ADDRESS; +export const resolveDidRegistryAddress = ( + didRegistryAddress = process.env.DID_REGISTRY_ADDRESS +): string => { + // const didRegistryAddress = process.env.DID_REGISTRY_ADDRESS; if (didRegistryAddress) { if (!ethers.utils.isAddress(didRegistryAddress)) { log.error( diff --git a/src/constants/networks/didRegistry.ts b/src/constants/did-web/lac/didRegistryAddresses.ts similarity index 65% rename from src/constants/networks/didRegistry.ts rename to src/constants/did-web/lac/didRegistryAddresses.ts index e20117a..c8988e6 100644 --- a/src/constants/networks/didRegistry.ts +++ b/src/constants/did-web/lac/didRegistryAddresses.ts @@ -1,6 +1,6 @@ export const REGISTRY: Map = new Map(); -REGISTRY.set('0x9e55c', ['0xBeB1df7Fb80CA88226aE1DaDa169639E950f3D79']); +REGISTRY.set('0x9e55c', ['0xA850839d7b37B8e18a9673213CC0f7400dDd8771']); export const DEFAULT_REGISTRY: Map = new Map(); -DEFAULT_REGISTRY.set('0x9e55c', '0xBeB1df7Fb80CA88226aE1DaDa169639E950f3D79'); +DEFAULT_REGISTRY.set('0x9e55c', '0xA850839d7b37B8e18a9673213CC0f7400dDd8771'); export const DOMAIN_NAMES: string[] = ['lacchain.id', 'another.lacchain.id']; export const DEFAULT_DOMAIN_NAME = 'lacchain.id'; diff --git a/src/constants/did-web/lac/lacDidRegistryAbi.ts b/src/constants/did-web/lac/lacDidRegistryAbi.ts new file mode 100644 index 0000000..5b47bb7 --- /dev/null +++ b/src/constants/did-web/lac/lacDidRegistryAbi.ts @@ -0,0 +1,425 @@ +export const LAC_DIDREGISTRY_RECOVERABLE_ABI = [ + { + inputs: [ + { + internalType: 'uint256', + name: '_minKeyRotationTime', + type: 'uint256' + } + ], + stateMutability: 'nonpayable', + type: 'constructor' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'identity', + type: 'address' + }, + { + indexed: false, + internalType: 'bytes', + name: 'name', + type: 'bytes' + }, + { + indexed: false, + internalType: 'bytes', + name: 'value', + type: 'bytes' + }, + { + indexed: false, + internalType: 'uint256', + name: 'validTo', + type: 'uint256' + }, + { + indexed: false, + internalType: 'uint256', + name: 'previousChange', + type: 'uint256' + } + ], + name: 'DIDAttributeChanged', + type: 'event' + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'identity', + type: 'address' + }, + { + indexed: false, + internalType: 'address', + name: 'controller', + type: 'address' + }, + { + indexed: false, + internalType: 'uint256', + name: 'previousChange', + type: 'uint256' + } + ], + name: 'DIDControllerChanged', + type: 'event' + }, + { + inputs: [ + { + internalType: 'address', + name: 'identity', + type: 'address' + }, + { + internalType: 'address', + name: 'controller', + type: 'address' + } + ], + name: 'addController', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'identity', + type: 'address' + }, + { + internalType: 'address', + name: 'newController', + type: 'address' + } + ], + name: 'changeController', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'identity', + type: 'address' + }, + { + internalType: 'uint8', + name: 'sigV', + type: 'uint8' + }, + { + internalType: 'bytes32', + name: 'sigR', + type: 'bytes32' + }, + { + internalType: 'bytes32', + name: 'sigS', + type: 'bytes32' + }, + { + internalType: 'address', + name: 'newController', + type: 'address' + } + ], + name: 'changeControllerSigned', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + name: 'changed', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address' + }, + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + name: 'controllers', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'identity', + type: 'address' + } + ], + name: 'disableKeyRotation', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'identity', + type: 'address' + }, + { + internalType: 'uint256', + name: 'keyRotationTime', + type: 'uint256' + } + ], + name: 'enableKeyRotation', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'subject', + type: 'address' + } + ], + name: 'getControllers', + outputs: [ + { + internalType: 'address[]', + name: '', + type: 'address[]' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'identity', + type: 'address' + } + ], + name: 'identityController', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address' + } + ], + name: 'nonce', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256' + } + ], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'identity', + type: 'address' + }, + { + internalType: 'address', + name: 'controller', + type: 'address' + } + ], + name: 'removeController', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'identity', + type: 'address' + }, + { + internalType: 'bytes', + name: 'name', + type: 'bytes' + }, + { + internalType: 'bytes', + name: 'value', + type: 'bytes' + } + ], + name: 'revokeAttribute', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'identity', + type: 'address' + }, + { + internalType: 'uint8', + name: 'sigV', + type: 'uint8' + }, + { + internalType: 'bytes32', + name: 'sigR', + type: 'bytes32' + }, + { + internalType: 'bytes32', + name: 'sigS', + type: 'bytes32' + }, + { + internalType: 'bytes', + name: 'name', + type: 'bytes' + }, + { + internalType: 'bytes', + name: 'value', + type: 'bytes' + } + ], + name: 'revokeAttributeSigned', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'identity', + type: 'address' + }, + { + internalType: 'bytes', + name: 'name', + type: 'bytes' + }, + { + internalType: 'bytes', + name: 'value', + type: 'bytes' + }, + { + internalType: 'uint256', + name: 'validity', + type: 'uint256' + } + ], + name: 'setAttribute', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + internalType: 'address', + name: 'identity', + type: 'address' + }, + { + internalType: 'uint8', + name: 'sigV', + type: 'uint8' + }, + { + internalType: 'bytes32', + name: 'sigR', + type: 'bytes32' + }, + { + internalType: 'bytes32', + name: 'sigS', + type: 'bytes32' + }, + { + internalType: 'bytes', + name: 'name', + type: 'bytes' + }, + { + internalType: 'bytes', + name: 'value', + type: 'bytes' + }, + { + internalType: 'uint256', + name: 'validity', + type: 'uint256' + } + ], + name: 'setAttributeSigned', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + } +]; diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts index 3d7e2fe..1186c3b 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -19,7 +19,8 @@ export enum ErrorsMessages { INTERNAL_SERVER_ERROR = 'Internal Server Error', BAD_REQUEST_ERROR = 'Bad request error', USER_ALREADY_EXISTS = 'A user with this email is already registered', - CREATE_KEY_ERROR = 'An internal server error occurred while trying to create a new key' + CREATE_KEY_ERROR = 'An internal server error occurred while trying to create a new key', + UNSUPPORTED_CHAIN_ID = 'Unsupported chain Id' } export const Errors = { From eafb55dd456aa7ce697ca086b293d68178d4296b Mon Sep 17 00:00:00 2001 From: eum602 Date: Thu, 27 Apr 2023 12:15:11 -0500 Subject: [PATCH 15/22] chore: add did web lac resolver and controller to return 'did controller' --- .../{ => did-lac}/did.controller.ts | 16 +- src/controllers/index.ts | 4 +- src/index.ts | 2 +- src/services/did-lac/did-registry.ts | 42 +++++ src/services/did-lac/did.service.ts | 177 ++++++++++++++++++ .../did-lac/interfaces/did-lac.service.ts | 5 + src/services/did.service.ts | 88 --------- 7 files changed, 240 insertions(+), 94 deletions(-) rename src/controllers/{ => did-lac}/did.controller.ts (57%) create mode 100644 src/services/did-lac/did-registry.ts create mode 100644 src/services/did-lac/did.service.ts create mode 100644 src/services/did-lac/interfaces/did-lac.service.ts delete mode 100644 src/services/did.service.ts diff --git a/src/controllers/did.controller.ts b/src/controllers/did-lac/did.controller.ts similarity index 57% rename from src/controllers/did.controller.ts rename to src/controllers/did-lac/did.controller.ts index db73f46..561496a 100644 --- a/src/controllers/did.controller.ts +++ b/src/controllers/did-lac/did.controller.ts @@ -1,11 +1,17 @@ -import { JsonController, Post, BadRequestError } from 'routing-controllers'; +import { + JsonController, + Post, + BadRequestError, + Get, + Param +} from 'routing-controllers'; import { Service } from 'typedi'; -import { DidServiceWebLac } from '@services/did.service'; +import { DidServiceWebLac } from '@services/did-lac/did.service'; import { ErrorsMessages } from '@constants/errorMessages'; @JsonController('/did-web-lac') @Service() -export class DidController { +export class DidWebLacController { constructor(private readonly didServiceWebLac: DidServiceWebLac) {} @Post() @@ -18,4 +24,8 @@ export class DidController { ); } } + @Get('/controller/:did') + async getDidController(@Param('did') did: string): Promise { + return this.didServiceWebLac.getController(did); + } } diff --git a/src/controllers/index.ts b/src/controllers/index.ts index 15c8654..82af98b 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1,3 +1,3 @@ -import { DidController } from './did.controller'; +import { DidWebLacController } from './did-lac/did.controller'; -export const controllers = [DidController]; +export const controllers = [DidWebLacController]; diff --git a/src/index.ts b/src/index.ts index e49614f..a96ad25 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -export { DidServiceWebLac } from './services/did.service'; +export { DidServiceWebLac } from './services/did-lac/did.service'; export { DidService, did as DidType } from './services/interfaces/did.service'; export { Did as DidEntity } from './entities/did.entity'; export { Secp256k1 as Secp256k1Entity } from 'lacpass-key-manager'; diff --git a/src/services/did-lac/did-registry.ts b/src/services/did-lac/did-registry.ts new file mode 100644 index 0000000..acb532e --- /dev/null +++ b/src/services/did-lac/did-registry.ts @@ -0,0 +1,42 @@ +import { ethers } from 'ethers'; +import { GasModelProvider, GasModelSigner } from '@lacchain/gas-model-provider'; +// eslint-disable-next-line max-len +import { LAC_DIDREGISTRY_RECOVERABLE_ABI } from '../../constants/did-web/lac/lacDidRegistryAbi'; +import { log4TSProvider } from '../../config'; + +export default class DIDRegistryContractInterface { + private registry: ethers.Contract; + log = log4TSProvider.getLogger('didRegistryService'); + constructor( + didRegistryAddress: string, + rpcUrl: string, + privateKey: string | undefined, + nodeAddress: string + ) { + if (!privateKey) { + // just for reading + privateKey = + '0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63'; + } + const provider = this.configureProvider(rpcUrl, privateKey, nodeAddress); + this.registry = new ethers.Contract( + didRegistryAddress, + LAC_DIDREGISTRY_RECOVERABLE_ABI, + provider + ); + } + + private configureProvider( + rpcUrl: string, + privateKey: string, + nodeAddress: string, + expiration = 1736394529 + ): GasModelSigner { + const provider = new GasModelProvider(rpcUrl); + return new GasModelSigner(privateKey, provider, nodeAddress, expiration); + } + + async lookupController(address: string) { + return this.registry.identityController(address); + } +} diff --git a/src/services/did-lac/did.service.ts b/src/services/did-lac/did.service.ts new file mode 100644 index 0000000..3f0bac2 --- /dev/null +++ b/src/services/did-lac/did.service.ts @@ -0,0 +1,177 @@ +import { Service } from 'typedi'; +import { getRepository } from 'typeorm'; +import { Did } from '../../entities/did.entity'; +import { KeyManagerService } from '../external/key-manager.service'; +import { EntityMapper } from '@clients/mapper/entityMapper.service'; +import { + CHAIN_ID, + getChainId, + log4TSProvider, + resolveDidDomainName, + resolveDidRegistryAddress, + getRpcUrl, + getNodeAddress +} from '../../config'; +import { keccak256 } from 'ethers/lib/utils'; +import DIDRegistryContractInterface from './did-registry'; +import { BadRequestError } from 'routing-controllers'; +import { DidLacService } from './interfaces/did-lac.service'; +import { ErrorsMessages } from '@constants/errorMessages'; + +export type didWelLacAttributes = { + address: string; + didRegistryAddress: string; + chainId: string; +}; + +@Service() +export class DidServiceWebLac implements DidLacService { + private readonly didRepository = getRepository(Did); + private readonly base58 = require('base-x')( + '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' + ); + private readonly hex = require('base-x')('0123456789abcdef'); + + private readonly didEncodingVersion = '0001'; // constant for encoding + // eslint-disable-next-line max-len + private readonly didType = '0001'; // constant + private readonly domainName: string; + + private readonly chainId: string; + private readonly didRegistryAddress: string; + private readonly rpcUrl: string; + private readonly nodeAddress: string; + + private readonly didLacWebIdentifier: string; + + private didRegistryContractInterface: DIDRegistryContractInterface; + + log = log4TSProvider.getLogger('didService'); + private keyManagerService: KeyManagerService; + constructor() { + this.keyManagerService = new KeyManagerService(); + this.chainId = getChainId(); + this.didRegistryAddress = resolveDidRegistryAddress(); + this.domainName = resolveDidDomainName(); + this.didLacWebIdentifier = `did:web:${this.domainName}:`; + this.rpcUrl = getRpcUrl(); + this.nodeAddress = getNodeAddress(); + this.didRegistryContractInterface = new DIDRegistryContractInterface( + this.didRegistryAddress, // base did registry + this.rpcUrl, + undefined, + this.nodeAddress + ); + } + addAttribute(_did: string, _rsaPublicKey: string): Promise { + throw new Error('Method not implemented.'); + } + + async getController(did: string): Promise { + const { address, chainId } = this.decodeDid(did); + console.log(address, chainId); + console.log(CHAIN_ID); + if (chainId.toLowerCase() !== CHAIN_ID.toLowerCase()) { + const message = ErrorsMessages.UNSUPPORTED_CHAIN_ID; + this.log.info(message); + throw new BadRequestError(message); + } + const didController = + await this.didRegistryContractInterface.lookupController(address); + return { controller: didController }; + } + + show(id: string) { + return this.didRepository.findOne(id); + } + + async createDid() { + const key = await this.keyManagerService.createSecp256k1Key(); + const did = EntityMapper.mapTo(Did, {}); + did.keyId = key.keyId; + did.did = + this.didLacWebIdentifier + + this.encode( + this.didType, + this.chainId, + key.address, + this.didRegistryAddress + ); + await this.didRepository.insert(did); + return { did: did.did }; + } + + private decodeDid(did: string): didWelLacAttributes { + const trimmed = did.replace(this.didLacWebIdentifier, ''); + const data = Buffer.from(this.base58.decode(trimmed)); + const len = data.length; + const encodedPayload = data.subarray(0, len - 4); + const computedChecksum = this.checksum([encodedPayload]); + const checksum = data.subarray(len - 4, len); + if (!computedChecksum.equals(checksum)) { + const message = 'Checksum mismatch'; + this.log.info(message); + throw new BadRequestError(message); + } + const version = data.subarray(0, 2); + const didType = data.subarray(2, 4); + if (version.toString('hex') !== this.didEncodingVersion) { + // TODO handle better versioning + const message = 'Unsupported encoding version'; + this.log.info(message); + throw new BadRequestError(message); + } + + if (didType.toString('hex') !== this.didType) { + // TODO handle better versioning + const message = 'Unsupported did type'; + this.log.info(message); + throw new BadRequestError(message); + } + // TODO: improve code organization: according to didType and version + const address = '0x' + data.subarray(4, 24).toString('hex'); + const didRegistryAddress = '0x' + data.subarray(24, 44).toString('hex'); + let c = data.subarray(44, len - 4).toString('hex'); + if (c[0] === '0') { + c = c.substring(1); + } + const chainId = '0x' + c; + return { address, didRegistryAddress, chainId }; + } + + // todo: validate parameters + private encode( + didType: string, + chainId: string, + address: string, + didRegistry: string + ): string { + const payload = [ + Buffer.from(this.didEncodingVersion, 'hex'), + Buffer.from(didType, 'hex'), + this.getLacchainDataBuffer(chainId, address, didRegistry) + ]; + payload.push(this.checksum(payload)); + return this.base58.encode(Buffer.concat(payload)); + } + + private getLacchainDataBuffer( + chainId: string, + address: string, + didRegistry: string + ): Buffer { + const dataArr = [ + Buffer.from(address.slice(2), 'hex'), + Buffer.from(didRegistry.slice(2), 'hex'), + this.hex.decode(chainId.slice(2), 'hex') + ]; + return Buffer.concat(dataArr); + } + + private checksum(payload: Buffer[]): Buffer { + return Buffer.from( + keccak256(Buffer.concat(payload)).replace('0x', ''), + 'hex' + ).subarray(0, 4); + } +} diff --git a/src/services/did-lac/interfaces/did-lac.service.ts b/src/services/did-lac/interfaces/did-lac.service.ts new file mode 100644 index 0000000..5ce423c --- /dev/null +++ b/src/services/did-lac/interfaces/did-lac.service.ts @@ -0,0 +1,5 @@ +import { DidService } from '../../interfaces/did.service'; + +export interface DidLacService extends DidService { + addAttribute(did: string, rsaPublicKey: string): Promise; +} diff --git a/src/services/did.service.ts b/src/services/did.service.ts deleted file mode 100644 index ec3e8b9..0000000 --- a/src/services/did.service.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Service } from 'typedi'; -import { getRepository } from 'typeorm'; -import { Did } from '../entities/did.entity'; -import { KeyManagerService } from './external/key-manager.service'; -import { EntityMapper } from '@clients/mapper/entityMapper.service'; -import { - getChainId, - log4TSProvider, - resolveDidDomainName, - resolveDidRegistryAddress -} from '../config'; -import { DidService } from './interfaces/did.service'; - -@Service() -export class DidServiceWebLac implements DidService { - private readonly didRepository = getRepository(Did); - private readonly base58 = require('base-x')( - '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' - ); - private readonly hex = require('base-x')('0123456789abcdef'); - - private readonly didEncodingVersion = '0001'; // constant for encoding - // eslint-disable-next-line max-len - private readonly didType = '0001'; // constant - private readonly domainName: string; - - private readonly chainId: string; - private readonly didRegistryAddress: string; - - private readonly didLacWebIdentifier: string; - - log = log4TSProvider.getLogger('didService'); - private keyManagerService; - constructor() { - this.keyManagerService = new KeyManagerService(); - this.chainId = getChainId(); - this.didRegistryAddress = resolveDidRegistryAddress(); - this.domainName = resolveDidDomainName(); - this.didLacWebIdentifier = `did:web:${this.domainName}:`; - } - - show(id: string) { - return this.didRepository.findOne(id); - } - - async createDid() { - const key = await this.keyManagerService.createSecp256k1Key(); - const did = EntityMapper.mapTo(Did, {}); - did.keyId = key.keyId; - did.did = - this.didLacWebIdentifier + - this.encode( - this.didType, - this.chainId, - key.address, - this.didRegistryAddress - ); - await this.didRepository.insert(did); - return { did: did.did }; - } - // todo: validate parameters - encode( - didType: string, - chainId: string, - primaryAddress: string, - didRegistry: string - ): string { - const payload = [ - Buffer.from(this.didEncodingVersion, 'hex'), - Buffer.from(didType, 'hex'), - this.getLacchainDataBuffer(chainId, primaryAddress, didRegistry) - ]; - return this.base58.encode(Buffer.concat(payload)); - } - - getLacchainDataBuffer( - chainId: string, - address: string, - didRegistry: string - ): Buffer { - const dataArr = [ - this.hex.decode(chainId.slice(2), 'hex'), - Buffer.from(address.slice(2), 'hex'), - Buffer.from(didRegistry.slice(2), 'hex') - ]; - return Buffer.concat(dataArr); - } -} From bb71c3903e38d7ccac472a43cc34ad922bdd1db2 Mon Sep 17 00:00:00 2001 From: eum602 Date: Thu, 27 Apr 2023 14:56:54 -0500 Subject: [PATCH 16/22] refactor: move did controller files to a separate foder --- src/controllers/{did-lac => did-web-lac}/did.controller.ts | 0 src/controllers/index.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/controllers/{did-lac => did-web-lac}/did.controller.ts (100%) diff --git a/src/controllers/did-lac/did.controller.ts b/src/controllers/did-web-lac/did.controller.ts similarity index 100% rename from src/controllers/did-lac/did.controller.ts rename to src/controllers/did-web-lac/did.controller.ts diff --git a/src/controllers/index.ts b/src/controllers/index.ts index 82af98b..fa1f9fb 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1,3 +1,3 @@ -import { DidWebLacController } from './did-lac/did.controller'; +import { DidWebLacController } from './did-web-lac/did.controller'; export const controllers = [DidWebLacController]; From 42f1a4a2f04dd10bdf6ee1e6b5975ae61fa00abc Mon Sep 17 00:00:00 2001 From: eum602 Date: Sat, 29 Apr 2023 01:36:53 -0500 Subject: [PATCH 17/22] feat: add lacchain/ethereum signing capabilities - add jwk attribute logic --- .example.env | 2 + package.json | 2 +- src/config/index.ts | 4 +- src/constants/errorMessages.ts | 2 + .../did-web-lac/delegate.controller.ts | 29 ++++++ src/controllers/did-web-lac/did.controller.ts | 4 + src/controllers/index.ts | 3 +- src/dto/did-web-lac/addAttributeDTO.ts | 17 ++++ .../did-web-lac/did-web-lac.interface.ts | 10 ++ src/interfaces/ethereum/transaction.ts | 9 ++ src/services/did-lac/did-registry.ts | 7 +- src/services/did-lac/did.service.ts | 56 ++++++++++- .../did-lac/lacchain/lacchain-ethers.ts | 42 ++++++++ src/services/external/key-manager.service.ts | 95 ++++++++++++++++++- yarn.lock | 11 ++- 15 files changed, 276 insertions(+), 17 deletions(-) create mode 100644 src/controllers/did-web-lac/delegate.controller.ts create mode 100644 src/dto/did-web-lac/addAttributeDTO.ts create mode 100644 src/interfaces/did-web-lac/did-web-lac.interface.ts create mode 100644 src/interfaces/ethereum/transaction.ts create mode 100644 src/services/did-lac/lacchain/lacchain-ethers.ts diff --git a/.example.env b/.example.env index 788d04b..f323f81 100644 --- a/.example.env +++ b/.example.env @@ -57,6 +57,8 @@ EMAIL_TRANSPORTER = AWS # IS_DEPENDENT_SERVICE = true # uncomment this only if the service communicates with the other components by way of external services KEY_MANAGER_BASE_URL=http://laccpass-key-manager/api/v1 SECP256K1_KEY=/secp256k1 +SECP256K1_SIGN_ETHEREUM_TRANSACTION=/secp256k1/sign/ethereum-tx +SECP256K1_SIGN_LACCHAIN_TRANSACTION=/secp256k1/sign/lacchain-tx # Did Registry CHAIN_ID = 0x9e55c diff --git a/package.json b/package.json index 7af193e..06fa874 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "express-rate-limit": "^6.3.0", "helmet": "^5.0.2", "jsonwebtoken": "^9.0.0", - "lacpass-key-manager": "^0.0.8", + "lacpass-key-manager": "^0.0.15", "morgan": "^1.10.0", "multer": "^1.4.4", "nodemailer": "^6.7.3", diff --git a/src/config/index.ts b/src/config/index.ts index 9cd99b6..c25b99f 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -148,5 +148,7 @@ export const { SENDGRID_API_KEY, KEY_MANAGER_BASE_URL, SECP256K1_KEY, - IS_DEPENDENT_SERVICE + IS_DEPENDENT_SERVICE, + SECP256K1_SIGN_ETHEREUM_TRANSACTION, + SECP256K1_SIGN_LACCHAIN_TRANSACTION } = process.env; diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts index 1186c3b..453a8bd 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -20,6 +20,8 @@ export enum ErrorsMessages { BAD_REQUEST_ERROR = 'Bad request error', USER_ALREADY_EXISTS = 'A user with this email is already registered', CREATE_KEY_ERROR = 'An internal server error occurred while trying to create a new key', + // eslint-disable-next-line max-len + SIGN_TRANSACTION_ERROR = 'An error occurred while trying to sign transaction against external service', UNSUPPORTED_CHAIN_ID = 'Unsupported chain Id' } diff --git a/src/controllers/did-web-lac/delegate.controller.ts b/src/controllers/did-web-lac/delegate.controller.ts new file mode 100644 index 0000000..2d0d487 --- /dev/null +++ b/src/controllers/did-web-lac/delegate.controller.ts @@ -0,0 +1,29 @@ +import { + JsonController, + Post, + BadRequestError, + Body +} from 'routing-controllers'; +import { Service } from 'typedi'; +import { DidServiceWebLac } from '../../services/did-lac/did.service'; +import { ErrorsMessages } from '../../constants/errorMessages'; +import { RsaJwkAttributeDTO } from '../../dto/did-web-lac/addAttributeDTO'; + +@JsonController('/did-web-lac/delegate') +@Service() +export class DidWebLacDelegateController { + constructor(private readonly didServiceWebLac: DidServiceWebLac) {} + + @Post() + async addJWKRSAAttribute( + @Body({ validate: true }) attribute: RsaJwkAttributeDTO + ): Promise { + try { + return this.didServiceWebLac.addJwkRSAAttribute(attribute); + } catch (error: any) { + throw new BadRequestError( + error.detail ?? error.message ?? ErrorsMessages.INTERNAL_SERVER_ERROR + ); + } + } +} diff --git a/src/controllers/did-web-lac/did.controller.ts b/src/controllers/did-web-lac/did.controller.ts index 561496a..44e314b 100644 --- a/src/controllers/did-web-lac/did.controller.ts +++ b/src/controllers/did-web-lac/did.controller.ts @@ -28,4 +28,8 @@ export class DidWebLacController { async getDidController(@Param('did') did: string): Promise { return this.didServiceWebLac.getController(did); } + @Get('/decode/:did') + async getDidParams(@Param('did') did: string): Promise { + return this.didServiceWebLac.decodeDid(did); + } } diff --git a/src/controllers/index.ts b/src/controllers/index.ts index fa1f9fb..4f025ea 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1,3 +1,4 @@ +import { DidWebLacDelegateController } from './did-web-lac/delegate.controller'; import { DidWebLacController } from './did-web-lac/did.controller'; -export const controllers = [DidWebLacController]; +export const controllers = [DidWebLacController, DidWebLacDelegateController]; diff --git a/src/dto/did-web-lac/addAttributeDTO.ts b/src/dto/did-web-lac/addAttributeDTO.ts new file mode 100644 index 0000000..dac1e28 --- /dev/null +++ b/src/dto/did-web-lac/addAttributeDTO.ts @@ -0,0 +1,17 @@ +import { IsObject, IsString } from 'class-validator'; + +export class RsaJwk { + @IsString() + kty!: string; + @IsString() + e!: string; + @IsString() + n!: string; +} + +export class RsaJwkAttributeDTO { + @IsString() + did!: string; + @IsObject() + rsaJwk!: RsaJwk; +} diff --git a/src/interfaces/did-web-lac/did-web-lac.interface.ts b/src/interfaces/did-web-lac/did-web-lac.interface.ts new file mode 100644 index 0000000..76f72cc --- /dev/null +++ b/src/interfaces/did-web-lac/did-web-lac.interface.ts @@ -0,0 +1,10 @@ +export interface IRsaAttribute { + did: string; + rsaJwk: RsaJwk; +} + +export interface RsaJwk { + kty: string; + e: string; + n: string; +} diff --git a/src/interfaces/ethereum/transaction.ts b/src/interfaces/ethereum/transaction.ts new file mode 100644 index 0000000..a19975b --- /dev/null +++ b/src/interfaces/ethereum/transaction.ts @@ -0,0 +1,9 @@ +export interface ITransaction { + from: string; + to: string; + data: string; +} + +export interface IEthereumTransactionResponse { + txHash: string; +} diff --git a/src/services/did-lac/did-registry.ts b/src/services/did-lac/did-registry.ts index acb532e..016c0ce 100644 --- a/src/services/did-lac/did-registry.ts +++ b/src/services/did-lac/did-registry.ts @@ -1,4 +1,4 @@ -import { ethers } from 'ethers'; +import { Wallet, ethers } from 'ethers'; import { GasModelProvider, GasModelSigner } from '@lacchain/gas-model-provider'; // eslint-disable-next-line max-len import { LAC_DIDREGISTRY_RECOVERABLE_ABI } from '../../constants/did-web/lac/lacDidRegistryAbi'; @@ -15,8 +15,7 @@ export default class DIDRegistryContractInterface { ) { if (!privateKey) { // just for reading - privateKey = - '0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63'; + privateKey = Wallet.createRandom().privateKey; } const provider = this.configureProvider(rpcUrl, privateKey, nodeAddress); this.registry = new ethers.Contract( @@ -30,7 +29,7 @@ export default class DIDRegistryContractInterface { rpcUrl: string, privateKey: string, nodeAddress: string, - expiration = 1736394529 + expiration = Math.floor(Date.now() / 1000) + 86400 * 1080 ): GasModelSigner { const provider = new GasModelProvider(rpcUrl); return new GasModelSigner(privateKey, provider, nodeAddress, expiration); diff --git a/src/services/did-lac/did.service.ts b/src/services/did-lac/did.service.ts index 3f0bac2..4e22f3f 100644 --- a/src/services/did-lac/did.service.ts +++ b/src/services/did-lac/did.service.ts @@ -12,11 +12,15 @@ import { getRpcUrl, getNodeAddress } from '../../config'; -import { keccak256 } from 'ethers/lib/utils'; +import { Interface, keccak256, toUtf8Bytes } from 'ethers/lib/utils'; import DIDRegistryContractInterface from './did-registry'; import { BadRequestError } from 'routing-controllers'; import { DidLacService } from './interfaces/did-lac.service'; -import { ErrorsMessages } from '@constants/errorMessages'; +import { ErrorsMessages } from '../../constants/errorMessages'; +import { IRsaAttribute } from 'src/interfaces/did-web-lac/did-web-lac.interface'; +import { ITransaction } from 'src/interfaces/ethereum/transaction'; +import { ethers } from 'ethers'; +import { LacchainLib } from './lacchain/lacchain-ethers'; export type didWelLacAttributes = { address: string; @@ -46,6 +50,8 @@ export class DidServiceWebLac implements DidLacService { private didRegistryContractInterface: DIDRegistryContractInterface; + private readonly lacchainLib: LacchainLib; + log = log4TSProvider.getLogger('didService'); private keyManagerService: KeyManagerService; constructor() { @@ -62,7 +68,43 @@ export class DidServiceWebLac implements DidLacService { undefined, this.nodeAddress ); + // TODO: factor providers in such way that did service is independent + this.lacchainLib = new LacchainLib(this.nodeAddress, this.rpcUrl); } + async addJwkRSAAttribute(jwkRsaAttribute: IRsaAttribute): Promise { + const { address, didRegistryAddress, chainId } = this.decodeDid( + jwkRsaAttribute.did + ); + if (chainId.toLowerCase() !== CHAIN_ID.toLowerCase()) { + const message = ErrorsMessages.UNSUPPORTED_CHAIN_ID; + this.log.info(message); + throw new BadRequestError(message); + } + const jwk = jwkRsaAttribute.rsaJwk; + const name = `${jwk.kty}/${jwk.e}`; // TODO: encode properly - write specs + const value = jwk.n; + const methodName = 'setAttribute'; + const validity = Math.floor(Date.now() / 1000) + 86400 * 365; + const setAttributeMethodSignature = [ + `function ${methodName}(address,bytes,bytes,uint256) public` + ]; + const setAttributeInterface = new Interface(setAttributeMethodSignature); + const encodedData = setAttributeInterface.encodeFunctionData(methodName, [ + address, + toUtf8Bytes(name), + toUtf8Bytes(value), + validity + ]); + const didControllerAddress = + await this.didRegistryContractInterface.lookupController(address); + const tx: ITransaction = { + from: didControllerAddress, + to: didRegistryAddress, + data: encodedData + }; + return this.lacchainLib.signAndSend(tx); + } + addAttribute(_did: string, _rsaPublicKey: string): Promise { throw new Error('Method not implemented.'); } @@ -101,7 +143,7 @@ export class DidServiceWebLac implements DidLacService { return { did: did.did }; } - private decodeDid(did: string): didWelLacAttributes { + decodeDid(did: string): didWelLacAttributes { const trimmed = did.replace(this.didLacWebIdentifier, ''); const data = Buffer.from(this.base58.decode(trimmed)); const len = data.length; @@ -129,8 +171,12 @@ export class DidServiceWebLac implements DidLacService { throw new BadRequestError(message); } // TODO: improve code organization: according to didType and version - const address = '0x' + data.subarray(4, 24).toString('hex'); - const didRegistryAddress = '0x' + data.subarray(24, 44).toString('hex'); + const address = ethers.utils.getAddress( + '0x' + data.subarray(4, 24).toString('hex') + ); + const didRegistryAddress = ethers.utils.getAddress( + '0x' + data.subarray(24, 44).toString('hex') + ); let c = data.subarray(44, len - 4).toString('hex'); if (c[0] === '0') { c = c.substring(1); diff --git a/src/services/did-lac/lacchain/lacchain-ethers.ts b/src/services/did-lac/lacchain/lacchain-ethers.ts new file mode 100644 index 0000000..fde31e8 --- /dev/null +++ b/src/services/did-lac/lacchain/lacchain-ethers.ts @@ -0,0 +1,42 @@ +import { log4TSProvider } from '../../../config'; +import { GasModelProvider } from '@lacchain/gas-model-provider'; +import { KeyManagerService } from '../../external/key-manager.service'; +import { BigNumber, ethers } from 'ethers'; +import { + IEthereumTransactionResponse, + ITransaction +} from 'src/interfaces/ethereum/transaction'; + +export class LacchainLib { + log = log4TSProvider.getLogger('lacchainUtils'); + private nodeAddress: string; + private readonly provider: ethers.providers.Provider; + private rpcUrl: string; + private keyManagerService: KeyManagerService; + constructor(nodeAddress: string, rpcUrl: string) { + this.keyManagerService = new KeyManagerService(); + this.nodeAddress = nodeAddress; + this.rpcUrl = rpcUrl; + this.provider = new GasModelProvider(this.rpcUrl); + } + async signAndSend(tx: ITransaction): Promise { + const voidSigner = new ethers.VoidSigner(tx.from, this.provider); + // Gas Limit is set to avoid failures + const fullyPopulatedTransactionRequest = + await voidSigner.populateTransaction({ ...tx, gasLimit: 4700000 }); + const f = fullyPopulatedTransactionRequest.gasPrice; + const s = BigNumber.from(f); + fullyPopulatedTransactionRequest.gasPrice = s.toHexString(); + const signedTx = await this.keyManagerService.signLacchainTransaction({ + fullyPopulatedTransactionRequest, + signerAddress: tx.from, + nodeAddress: this.nodeAddress, + expiration: Math.floor(Date.now() / 1000) + 86400 * 4 // 4 days + }); + const txResponse = await this.provider.sendTransaction( + signedTx.signedTransaction + ); + this.log.info('Transaction successfully sent, txHash', txResponse.hash); + return { txHash: txResponse.hash }; + } +} diff --git a/src/services/external/key-manager.service.ts b/src/services/external/key-manager.service.ts index 8a8207d..87ca869 100644 --- a/src/services/external/key-manager.service.ts +++ b/src/services/external/key-manager.service.ts @@ -4,16 +4,34 @@ import { IS_DEPENDENT_SERVICE, KEY_MANAGER_BASE_URL, SECP256K1_KEY, + SECP256K1_SIGN_ETHEREUM_TRANSACTION, + SECP256K1_SIGN_LACCHAIN_TRANSACTION, log4TSProvider } from '@config'; -import { Secp256k1Service } from 'lacpass-key-manager'; +import { + IEthereumTransaction, + Secp256k1Service, + Secp256k1SignTransactionService, + Secp256k1SignLacchainTransactionService, + ISignedTransaction, + ILacchainTransaction +} from 'lacpass-key-manager'; import { InternalServerError } from 'routing-controllers'; import { ErrorsMessages } from '@constants/errorMessages'; import { ISecp256k1 } from 'src/interfaces/key/key.interface'; @Service() export class KeyManagerService { public createSecp256k1Key: () => Promise; + public signEthereumTransaction: ( + ethereumTransaction: IEthereumTransaction + ) => Promise; + public signLacchainTransaction: ( + lacchainTransaction: ILacchainTransaction + ) => Promise; private secp256k1Service: Secp256k1Service | null; + private secp256k1SignTransactionService: Secp256k1SignTransactionService | null; + // eslint-disable-next-line max-len + private secp256k1SignLacchainTransactionService: Secp256k1SignLacchainTransactionService | null; log = log4TSProvider.getLogger('KeyManagerService'); constructor() { if (IS_DEPENDENT_SERVICE !== 'true') { @@ -21,10 +39,28 @@ export class KeyManagerService { this.createSecp256k1Key = this.createSecp256k1KeyByLib; const S = require('lacpass-key-manager').Secp256k1DbService; this.secp256k1Service = new S(); + + this.signEthereumTransaction = this.signEthereumTransactionByLib; + const T = + require('lacpass-key-manager').Secp256k1SignTransactionServiceDb; + this.secp256k1SignTransactionService = new T(); + + this.signLacchainTransaction = this.signLacchainTransactionByLib; + const R = + require('lacpass-key-manager').Secp256k1SignLacchainTransactionServiceDb; + this.secp256k1SignLacchainTransactionService = new R(); } else { this.log.info('Configuring key-manager external service connection'); this.secp256k1Service = null; this.createSecp256k1Key = this.createKeyByExternalService; + + this.secp256k1SignTransactionService = null; + this.signEthereumTransaction = + this.secp256k1SignTransactionByExternalService; + + this.secp256k1SignLacchainTransactionService = null; + this.signLacchainTransaction = + this.signLacchainTransactionByExternalService; } } async createSecp256k1KeyByLib(): Promise { @@ -44,4 +80,61 @@ export class KeyManagerService { } return (await result.json()) as ISecp256k1; } + + async signEthereumTransactionByLib( + ethereumTransaction: IEthereumTransaction + ): Promise { + return this.secp256k1SignTransactionService?.signEthereumBasedTransaction( + ethereumTransaction + ); + } + async secp256k1SignTransactionByExternalService( + ethereumTransaction: IEthereumTransaction + ): Promise { + const result = await fetch( + `${KEY_MANAGER_BASE_URL}${SECP256K1_SIGN_ETHEREUM_TRANSACTION}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(ethereumTransaction) + } + ); + console.log('status', result.status); + if (result.status !== 200) { + console.log(await result.text()); + throw new InternalServerError(ErrorsMessages.SIGN_TRANSACTION_ERROR); + } + return (await result.json()) as ISignedTransaction; // todo: check type in this return + } + + async signLacchainTransactionByLib( + lacchainTransaction: ILacchainTransaction + ): Promise { + return this.secp256k1SignLacchainTransactionService?.signEthereumBasedTransaction( + lacchainTransaction + ); + } + + async signLacchainTransactionByExternalService( + lacchainTransaction: ILacchainTransaction + ): Promise { + const result = await fetch( + `${KEY_MANAGER_BASE_URL}${SECP256K1_SIGN_LACCHAIN_TRANSACTION}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(lacchainTransaction) + } + ); + console.log('status', result.status); + if (result.status !== 200) { + console.log(await result.text()); + throw new InternalServerError(ErrorsMessages.SIGN_TRANSACTION_ERROR); + } + return (await result.json()) as ISignedTransaction; // todo: check type in this return + } } diff --git a/yarn.lock b/yarn.lock index d30d70b..f33e175 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5210,11 +5210,12 @@ koa@^2.8.2: type-is "^1.6.16" vary "^1.1.2" -lacpass-key-manager@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/lacpass-key-manager/-/lacpass-key-manager-0.0.8.tgz#28ec13eab00a1affe0ea725e0826b6f02c8db333" - integrity sha512-62dd6sMijVox/ydbjoFvRHjwtr4rXZD6T1YWhqb7wIWloL5Jro/8YIsThD0J/v1RY1NiOiimPdJDrpj4LD6ejA== +lacpass-key-manager@^0.0.15: + version "0.0.15" + resolved "https://registry.yarnpkg.com/lacpass-key-manager/-/lacpass-key-manager-0.0.15.tgz#f227f6dc564716c7aede62043d7ea41e83300e00" + integrity sha512-f3FbSbHZEKaw/TSVWYhROCe1thEttljgUqLLOxIMddq/useJQV/UO9P8mjfta0+ALpVHHWJAwnC6IJ8yO7KaLg== dependencies: + "@lacchain/gas-model-provider" "^1.0.1" aws-sdk "^2.1116.0" bcrypt "^5.1.0" body-parser "^1.20.0" @@ -5245,6 +5246,8 @@ lacpass-key-manager@^0.0.8: typedi "^0.10.0" typeorm "^0.2.41" typeorm-seeding "^1.6.1" + typescript-logging "^2.1.0" + typescript-logging-log4ts-style "^2.1.0" latest-version@^5.1.0: version "5.1.0" From 408bb092d2eed537a1dacd81f015fdc4d4e1e6a7 Mon Sep 17 00:00:00 2001 From: eum602 Date: Sun, 30 Apr 2023 17:09:05 -0500 Subject: [PATCH 18/22] chore: add cbor encoding on adding jwk attribute --- package.json | 1 + .../lac/didVerificationMethodParams.ts | 2 + src/constants/errorMessages.ts | 5 +- .../did-web-lac/delegate.controller.ts | 28 +++++-- src/controllers/index.ts | 4 +- src/dto/did-web-lac/addAttributeDTO.ts | 28 ++++++- .../did-web-lac/did-web-lac.interface.ts | 21 ++++++ src/services/did-lac/did.service.ts | 75 +++++++++++++++++-- yarn.lock | 12 +++ 9 files changed, 158 insertions(+), 18 deletions(-) create mode 100644 src/constants/did-web/lac/didVerificationMethodParams.ts diff --git a/package.json b/package.json index 06fa874..b38005d 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "base-x": "^4.0.0", "bcrypt": "^5.1.0", "body-parser": "^1.20.0", + "cbor": "^8.1.0", "class-transformer": "^0.5.1", "class-validator": "^0.12.2", "class-validator-jsonschema": "^2.2.0", diff --git a/src/constants/did-web/lac/didVerificationMethodParams.ts b/src/constants/did-web/lac/didVerificationMethodParams.ts new file mode 100644 index 0000000..1bba223 --- /dev/null +++ b/src/constants/did-web/lac/didVerificationMethodParams.ts @@ -0,0 +1,2 @@ +export const VM_RELATIONS: Map = new Map(); +VM_RELATIONS.set('asse', true); diff --git a/src/constants/errorMessages.ts b/src/constants/errorMessages.ts index 453a8bd..ee2c862 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -22,7 +22,10 @@ export enum ErrorsMessages { CREATE_KEY_ERROR = 'An internal server error occurred while trying to create a new key', // eslint-disable-next-line max-len SIGN_TRANSACTION_ERROR = 'An error occurred while trying to sign transaction against external service', - UNSUPPORTED_CHAIN_ID = 'Unsupported chain Id' + UNSUPPORTED_CHAIN_ID = 'Unsupported chain Id', + INVALID_EXPIRATION_DAYS = 'Valid days must be greater than zero', + INVALID_JWK_TYPE = 'Invalid Jwk type', + INVALID_VM_RELATION_TYPE = 'Invalid verification method relation type' } export const Errors = { diff --git a/src/controllers/did-web-lac/delegate.controller.ts b/src/controllers/did-web-lac/delegate.controller.ts index 2d0d487..71a22cd 100644 --- a/src/controllers/did-web-lac/delegate.controller.ts +++ b/src/controllers/did-web-lac/delegate.controller.ts @@ -7,19 +7,35 @@ import { import { Service } from 'typedi'; import { DidServiceWebLac } from '../../services/did-lac/did.service'; import { ErrorsMessages } from '../../constants/errorMessages'; -import { RsaJwkAttributeDTO } from '../../dto/did-web-lac/addAttributeDTO'; +import { + EcJwkAttributeDTO, + RsaJwkAttributeDTO +} from '../../dto/did-web-lac/addAttributeDTO'; -@JsonController('/did-web-lac/delegate') +@JsonController('/did-web-lac/attribute') @Service() -export class DidWebLacDelegateController { +export class DidWebLacAttributeController { constructor(private readonly didServiceWebLac: DidServiceWebLac) {} - @Post() - async addJWKRSAAttribute( + @Post('/add/jwk/rsa') + async addRsaJwkAttribute( @Body({ validate: true }) attribute: RsaJwkAttributeDTO ): Promise { try { - return this.didServiceWebLac.addJwkRSAAttribute(attribute); + return this.didServiceWebLac.addRsaJwkAttribute(attribute); + } catch (error: any) { + throw new BadRequestError( + error.detail ?? error.message ?? ErrorsMessages.INTERNAL_SERVER_ERROR + ); + } + } + + @Post('/add/jwk/ec') + async addEcJwkAttribute( + @Body({ validate: true }) attribute: EcJwkAttributeDTO + ): Promise { + try { + return this.didServiceWebLac.addEcJwkAttribute(attribute); } catch (error: any) { throw new BadRequestError( error.detail ?? error.message ?? ErrorsMessages.INTERNAL_SERVER_ERROR diff --git a/src/controllers/index.ts b/src/controllers/index.ts index 4f025ea..5d3a7c6 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1,4 +1,4 @@ -import { DidWebLacDelegateController } from './did-web-lac/delegate.controller'; +import { DidWebLacAttributeController } from './did-web-lac/delegate.controller'; import { DidWebLacController } from './did-web-lac/did.controller'; -export const controllers = [DidWebLacController, DidWebLacDelegateController]; +export const controllers = [DidWebLacController, DidWebLacAttributeController]; diff --git a/src/dto/did-web-lac/addAttributeDTO.ts b/src/dto/did-web-lac/addAttributeDTO.ts index dac1e28..e4d0f1f 100644 --- a/src/dto/did-web-lac/addAttributeDTO.ts +++ b/src/dto/did-web-lac/addAttributeDTO.ts @@ -1,4 +1,4 @@ -import { IsObject, IsString } from 'class-validator'; +import { IsNumber, IsObject, IsString } from 'class-validator'; export class RsaJwk { @IsString() @@ -9,9 +9,35 @@ export class RsaJwk { n!: string; } +export class EcJwk { + @IsString() + kty!: string; + @IsString() + x!: string; + @IsString() + y!: string; + @IsString() + crv!: string; +} + export class RsaJwkAttributeDTO { @IsString() did!: string; + @IsNumber() + validDays!: number; + @IsString() + relation!: string; @IsObject() rsaJwk!: RsaJwk; } + +export class EcJwkAttributeDTO { + @IsString() + did!: string; + @IsNumber() + validDays!: number; + @IsString() + relation!: string; + @IsObject() + ecJwk!: EcJwk; +} diff --git a/src/interfaces/did-web-lac/did-web-lac.interface.ts b/src/interfaces/did-web-lac/did-web-lac.interface.ts index 76f72cc..00fbd46 100644 --- a/src/interfaces/did-web-lac/did-web-lac.interface.ts +++ b/src/interfaces/did-web-lac/did-web-lac.interface.ts @@ -1,6 +1,14 @@ +export interface IJwkAttribute { + did: string; + jwk: any; + validDays: number; + relation: string; +} export interface IRsaAttribute { did: string; rsaJwk: RsaJwk; + validDays: number; + relation: string; } export interface RsaJwk { @@ -8,3 +16,16 @@ export interface RsaJwk { e: string; n: string; } + +export interface EcJwk { + kty: string; + x: string; + y: string; + crv: string; +} +export interface IJwkEcAttribute { + did: string; + ecJwk: EcJwk; + validDays: number; + relation: string; +} diff --git a/src/services/did-lac/did.service.ts b/src/services/did-lac/did.service.ts index 4e22f3f..1e68517 100644 --- a/src/services/did-lac/did.service.ts +++ b/src/services/did-lac/did.service.ts @@ -17,10 +17,16 @@ import DIDRegistryContractInterface from './did-registry'; import { BadRequestError } from 'routing-controllers'; import { DidLacService } from './interfaces/did-lac.service'; import { ErrorsMessages } from '../../constants/errorMessages'; -import { IRsaAttribute } from 'src/interfaces/did-web-lac/did-web-lac.interface'; +import { + IJwkAttribute, + IJwkEcAttribute, + IRsaAttribute +} from 'src/interfaces/did-web-lac/did-web-lac.interface'; import { ITransaction } from 'src/interfaces/ethereum/transaction'; import { ethers } from 'ethers'; import { LacchainLib } from './lacchain/lacchain-ethers'; +import { encode } from 'cbor'; +import { VM_RELATIONS } from '../../constants/did-web/lac/didVerificationMethodParams'; export type didWelLacAttributes = { address: string; @@ -71,20 +77,73 @@ export class DidServiceWebLac implements DidLacService { // TODO: factor providers in such way that did service is independent this.lacchainLib = new LacchainLib(this.nodeAddress, this.rpcUrl); } - async addJwkRSAAttribute(jwkRsaAttribute: IRsaAttribute): Promise { + async addRsaJwkAttribute(jwkRsaAttribute: IRsaAttribute): Promise { + // TODO: validate RSA params + const { kty } = jwkRsaAttribute.rsaJwk; + if (kty !== 'RSA') { + const message = ErrorsMessages.INVALID_JWK_TYPE; + this.log.info(message); + throw new BadRequestError(message); + } + const jwkAttribute: IJwkAttribute = { + did: jwkRsaAttribute.did, + jwk: jwkRsaAttribute.rsaJwk, + validDays: jwkRsaAttribute.validDays, + relation: jwkRsaAttribute.relation + }; + return this._addJwkAttribute(jwkAttribute); + } + + async addEcJwkAttribute(ecJwkAttribute: IJwkEcAttribute): Promise { + // TODO: validate RSA params + const { kty } = ecJwkAttribute.ecJwk; + if (kty !== 'EC') { + const message = ErrorsMessages.INVALID_JWK_TYPE; + this.log.info(message); + throw new BadRequestError(message); + } + const jwkAttribute: IJwkAttribute = { + did: ecJwkAttribute.did, + jwk: ecJwkAttribute.ecJwk, + validDays: ecJwkAttribute.validDays, + relation: ecJwkAttribute.relation + }; + return this._addJwkAttribute(jwkAttribute); + } + + private async _addJwkAttribute(jwkAttribute: IJwkAttribute): Promise { const { address, didRegistryAddress, chainId } = this.decodeDid( - jwkRsaAttribute.did + jwkAttribute.did ); if (chainId.toLowerCase() !== CHAIN_ID.toLowerCase()) { const message = ErrorsMessages.UNSUPPORTED_CHAIN_ID; this.log.info(message); throw new BadRequestError(message); } - const jwk = jwkRsaAttribute.rsaJwk; - const name = `${jwk.kty}/${jwk.e}`; // TODO: encode properly - write specs - const value = jwk.n; + const validDays = jwkAttribute.validDays; + if (validDays < 1) { + const message = ErrorsMessages.INVALID_EXPIRATION_DAYS; + this.log.info(message); + throw new BadRequestError(message); + } + + const { relation } = jwkAttribute; + if (!VM_RELATIONS.get(relation)) { + const message = ErrorsMessages.INVALID_VM_RELATION_TYPE; + this.log.info(message); + throw new BadRequestError(message); + } + const { jwk } = jwkAttribute; + const algorithm = 'JsonWebKey2020'; + const didPrimaryAddress = address; // found 'controller' in lacchain specs + const encodingMethod = 'cbor'; + + // TODO: check if 'controller' (didPrimaryAddress) is really needed + // asse/did/JsonWebKey2020/cbor + const name = `${relation}/${didPrimaryAddress}/${algorithm}/${encodingMethod}`; + const value = encode(jwk); const methodName = 'setAttribute'; - const validity = Math.floor(Date.now() / 1000) + 86400 * 365; + const validity = Math.floor(Date.now() / 1000) + 86400 * validDays; const setAttributeMethodSignature = [ `function ${methodName}(address,bytes,bytes,uint256) public` ]; @@ -92,7 +151,7 @@ export class DidServiceWebLac implements DidLacService { const encodedData = setAttributeInterface.encodeFunctionData(methodName, [ address, toUtf8Bytes(name), - toUtf8Bytes(value), + value, validity ]); const didControllerAddress = diff --git a/yarn.lock b/yarn.lock index f33e175..c45973e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2137,6 +2137,13 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +cbor@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" + integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== + dependencies: + nofilter "^3.1.0" + chai@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/chai/-/chai-2.3.0.tgz#8a2f6a34748da801090fd73287b2aa739a4e909a" @@ -5753,6 +5760,11 @@ nodemon@^2.0.15: undefsafe "^2.0.5" update-notifier "^5.1.0" +nofilter@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== + nopt@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" From 15add34b24eaf034479520add50fe31075a6ed52 Mon Sep 17 00:00:00 2001 From: eum602 Date: Sun, 30 Apr 2023 23:23:26 -0500 Subject: [PATCH 19/22] chore: update class names --- package.json | 2 +- ...gate.controller.ts => attribute.controller.ts} | 0 src/controllers/index.ts | 2 +- src/dto/did-web-lac/addAttributeDTO.ts | 8 ++++---- src/index.ts | 6 ++++++ .../did-web-lac/did-web-lac.interface.ts | 2 +- src/services/did-lac/did.service.ts | 15 ++++++--------- .../did-lac/interfaces/did-lac.service.ts | 13 +++++++++++++ 8 files changed, 32 insertions(+), 16 deletions(-) rename src/controllers/did-web-lac/{delegate.controller.ts => attribute.controller.ts} (100%) diff --git a/package.json b/package.json index b38005d..a8f3656 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lacpass-identity", - "version": "0.0.8", + "version": "0.0.10", "description": "Rest api for laccpass identity manager", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", diff --git a/src/controllers/did-web-lac/delegate.controller.ts b/src/controllers/did-web-lac/attribute.controller.ts similarity index 100% rename from src/controllers/did-web-lac/delegate.controller.ts rename to src/controllers/did-web-lac/attribute.controller.ts diff --git a/src/controllers/index.ts b/src/controllers/index.ts index 5d3a7c6..6d5c8f8 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1,4 +1,4 @@ -import { DidWebLacAttributeController } from './did-web-lac/delegate.controller'; +import { DidWebLacAttributeController } from './did-web-lac/attribute.controller'; import { DidWebLacController } from './did-web-lac/did.controller'; export const controllers = [DidWebLacController, DidWebLacAttributeController]; diff --git a/src/dto/did-web-lac/addAttributeDTO.ts b/src/dto/did-web-lac/addAttributeDTO.ts index e4d0f1f..cc3d18b 100644 --- a/src/dto/did-web-lac/addAttributeDTO.ts +++ b/src/dto/did-web-lac/addAttributeDTO.ts @@ -1,6 +1,6 @@ import { IsNumber, IsObject, IsString } from 'class-validator'; -export class RsaJwk { +export class RsaJwkDTO { @IsString() kty!: string; @IsString() @@ -9,7 +9,7 @@ export class RsaJwk { n!: string; } -export class EcJwk { +export class EcJwkDTO { @IsString() kty!: string; @IsString() @@ -28,7 +28,7 @@ export class RsaJwkAttributeDTO { @IsString() relation!: string; @IsObject() - rsaJwk!: RsaJwk; + rsaJwk!: RsaJwkDTO; } export class EcJwkAttributeDTO { @@ -39,5 +39,5 @@ export class EcJwkAttributeDTO { @IsString() relation!: string; @IsObject() - ecJwk!: EcJwk; + ecJwk!: EcJwkDTO; } diff --git a/src/index.ts b/src/index.ts index a96ad25..de02784 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,10 @@ export { DidServiceWebLac } from './services/did-lac/did.service'; +export { + didWelLacAttributes, + DidLacService +} from './services/did-lac/interfaces/did-lac.service'; export { DidService, did as DidType } from './services/interfaces/did.service'; export { Did as DidEntity } from './entities/did.entity'; export { Secp256k1 as Secp256k1Entity } from 'lacpass-key-manager'; +export * from './dto/did-web-lac/addAttributeDTO'; +export * from './interfaces/did-web-lac/did-web-lac.interface'; diff --git a/src/interfaces/did-web-lac/did-web-lac.interface.ts b/src/interfaces/did-web-lac/did-web-lac.interface.ts index 00fbd46..c1d91b6 100644 --- a/src/interfaces/did-web-lac/did-web-lac.interface.ts +++ b/src/interfaces/did-web-lac/did-web-lac.interface.ts @@ -4,7 +4,7 @@ export interface IJwkAttribute { validDays: number; relation: string; } -export interface IRsaAttribute { +export interface IJwkRsaAttribute { did: string; rsaJwk: RsaJwk; validDays: number; diff --git a/src/services/did-lac/did.service.ts b/src/services/did-lac/did.service.ts index 1e68517..7638db0 100644 --- a/src/services/did-lac/did.service.ts +++ b/src/services/did-lac/did.service.ts @@ -15,12 +15,15 @@ import { import { Interface, keccak256, toUtf8Bytes } from 'ethers/lib/utils'; import DIDRegistryContractInterface from './did-registry'; import { BadRequestError } from 'routing-controllers'; -import { DidLacService } from './interfaces/did-lac.service'; +import { + DidLacService, + didWelLacAttributes +} from './interfaces/did-lac.service'; import { ErrorsMessages } from '../../constants/errorMessages'; import { IJwkAttribute, IJwkEcAttribute, - IRsaAttribute + IJwkRsaAttribute } from 'src/interfaces/did-web-lac/did-web-lac.interface'; import { ITransaction } from 'src/interfaces/ethereum/transaction'; import { ethers } from 'ethers'; @@ -28,12 +31,6 @@ import { LacchainLib } from './lacchain/lacchain-ethers'; import { encode } from 'cbor'; import { VM_RELATIONS } from '../../constants/did-web/lac/didVerificationMethodParams'; -export type didWelLacAttributes = { - address: string; - didRegistryAddress: string; - chainId: string; -}; - @Service() export class DidServiceWebLac implements DidLacService { private readonly didRepository = getRepository(Did); @@ -77,7 +74,7 @@ export class DidServiceWebLac implements DidLacService { // TODO: factor providers in such way that did service is independent this.lacchainLib = new LacchainLib(this.nodeAddress, this.rpcUrl); } - async addRsaJwkAttribute(jwkRsaAttribute: IRsaAttribute): Promise { + async addRsaJwkAttribute(jwkRsaAttribute: IJwkRsaAttribute): Promise { // TODO: validate RSA params const { kty } = jwkRsaAttribute.rsaJwk; if (kty !== 'RSA') { diff --git a/src/services/did-lac/interfaces/did-lac.service.ts b/src/services/did-lac/interfaces/did-lac.service.ts index 5ce423c..30aa3da 100644 --- a/src/services/did-lac/interfaces/did-lac.service.ts +++ b/src/services/did-lac/interfaces/did-lac.service.ts @@ -1,5 +1,18 @@ +import { + IJwkEcAttribute, + IJwkRsaAttribute +} from 'src/interfaces/did-web-lac/did-web-lac.interface'; import { DidService } from '../../interfaces/did.service'; +export type didWelLacAttributes = { + address: string; + didRegistryAddress: string; + chainId: string; +}; export interface DidLacService extends DidService { addAttribute(did: string, rsaPublicKey: string): Promise; + addRsaJwkAttribute(jwkRsaAttribute: IJwkRsaAttribute): Promise; + addEcJwkAttribute(ecJwkAttribute: IJwkEcAttribute): Promise; + getController(did: string): Promise; + decodeDid(did: string): didWelLacAttributes; } From d405f809972fd39fe32f59f5ae36cac06a06510b Mon Sep 17 00:00:00 2001 From: eum602 Date: Fri, 26 May 2023 00:06:13 -0500 Subject: [PATCH 20/22] feat: add 'lac1' did method --- .../did-lac1/attribute.controller.ts | 45 +++++++++++++++++++ src/controllers/did-lac1/did.controller.ts | 35 +++++++++++++++ .../did-web-lac/attribute.controller.ts | 2 +- ...ontroller.ts => did.web.lac.controller.ts} | 2 +- src/controllers/index.ts | 8 ++-- src/index.ts | 2 +- src/services/did-lac/did.service.ts | 15 +++---- src/services/did-lac/didLac1.service.ts | 9 ++++ src/services/did-lac/didWebLac.service.ts | 11 +++++ 9 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 src/controllers/did-lac1/attribute.controller.ts create mode 100644 src/controllers/did-lac1/did.controller.ts rename src/controllers/did-web-lac/{did.controller.ts => did.web.lac.controller.ts} (92%) create mode 100644 src/services/did-lac/didLac1.service.ts create mode 100644 src/services/did-lac/didWebLac.service.ts diff --git a/src/controllers/did-lac1/attribute.controller.ts b/src/controllers/did-lac1/attribute.controller.ts new file mode 100644 index 0000000..4d5ef20 --- /dev/null +++ b/src/controllers/did-lac1/attribute.controller.ts @@ -0,0 +1,45 @@ +import { + JsonController, + Post, + BadRequestError, + Body +} from 'routing-controllers'; +import { Service } from 'typedi'; +import { ErrorsMessages } from '../../constants/errorMessages'; +import { + EcJwkAttributeDTO, + RsaJwkAttributeDTO +} from '../../dto/did-web-lac/addAttributeDTO'; +import { DidLac1Service } from '@services/did-lac/didLac1.service'; + +@JsonController('/did-lac1/attribute') +@Service() +export class DidLac1AttributeController { + constructor(private readonly didService: DidLac1Service) {} + + @Post('/add/jwk/rsa') + async addRsaJwkAttribute( + @Body({ validate: true }) attribute: RsaJwkAttributeDTO + ): Promise { + try { + return this.didService.addRsaJwkAttribute(attribute); + } catch (error: any) { + throw new BadRequestError( + error.detail ?? error.message ?? ErrorsMessages.INTERNAL_SERVER_ERROR + ); + } + } + + @Post('/add/jwk/ec') + async addEcJwkAttribute( + @Body({ validate: true }) attribute: EcJwkAttributeDTO + ): Promise { + try { + return this.didService.addEcJwkAttribute(attribute); + } catch (error: any) { + throw new BadRequestError( + error.detail ?? error.message ?? ErrorsMessages.INTERNAL_SERVER_ERROR + ); + } + } +} diff --git a/src/controllers/did-lac1/did.controller.ts b/src/controllers/did-lac1/did.controller.ts new file mode 100644 index 0000000..09db15d --- /dev/null +++ b/src/controllers/did-lac1/did.controller.ts @@ -0,0 +1,35 @@ +import { + JsonController, + Post, + BadRequestError, + Get, + Param +} from 'routing-controllers'; +import { Service } from 'typedi'; +import { DidLac1Service } from '@services/did-lac/didLac1.service'; +import { ErrorsMessages } from '@constants/errorMessages'; + +@JsonController('/did-lac1') +@Service() +export class DidLac1Controller { + constructor(private readonly didService: DidLac1Service) {} + + @Post() + async createDidLac1(): Promise { + try { + return this.didService.createDid(); + } catch (error: any) { + throw new BadRequestError( + error.detail ?? error.message ?? ErrorsMessages.INTERNAL_SERVER_ERROR + ); + } + } + @Get('/controller/:did') + async getDidController(@Param('did') did: string): Promise { + return this.didService.getController(did); + } + @Get('/decode/:did') + async getDidParams(@Param('did') did: string): Promise { + return this.didService.decodeDid(did); + } +} diff --git a/src/controllers/did-web-lac/attribute.controller.ts b/src/controllers/did-web-lac/attribute.controller.ts index 71a22cd..5c2e79f 100644 --- a/src/controllers/did-web-lac/attribute.controller.ts +++ b/src/controllers/did-web-lac/attribute.controller.ts @@ -5,7 +5,7 @@ import { Body } from 'routing-controllers'; import { Service } from 'typedi'; -import { DidServiceWebLac } from '../../services/did-lac/did.service'; +import { DidServiceWebLac } from '../../services/did-lac/didWebLac.service'; import { ErrorsMessages } from '../../constants/errorMessages'; import { EcJwkAttributeDTO, diff --git a/src/controllers/did-web-lac/did.controller.ts b/src/controllers/did-web-lac/did.web.lac.controller.ts similarity index 92% rename from src/controllers/did-web-lac/did.controller.ts rename to src/controllers/did-web-lac/did.web.lac.controller.ts index 44e314b..c2f336b 100644 --- a/src/controllers/did-web-lac/did.controller.ts +++ b/src/controllers/did-web-lac/did.web.lac.controller.ts @@ -6,7 +6,7 @@ import { Param } from 'routing-controllers'; import { Service } from 'typedi'; -import { DidServiceWebLac } from '@services/did-lac/did.service'; +import { DidServiceWebLac } from '@services/did-lac/didWebLac.service'; import { ErrorsMessages } from '@constants/errorMessages'; @JsonController('/did-web-lac') diff --git a/src/controllers/index.ts b/src/controllers/index.ts index 6d5c8f8..60ef9da 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1,4 +1,6 @@ -import { DidWebLacAttributeController } from './did-web-lac/attribute.controller'; -import { DidWebLacController } from './did-web-lac/did.controller'; +// import { DidWebLacController } from './did-web-lac/did.web.lac.controller'; +// import { DidWebLacAttributeController } from './did-web-lac/attribute.controller'; +import { DidLac1AttributeController } from './did-lac1/attribute.controller'; +import { DidLac1Controller } from './did-lac1/did.controller'; -export const controllers = [DidWebLacController, DidWebLacAttributeController]; +export const controllers = [DidLac1Controller, DidLac1AttributeController]; diff --git a/src/index.ts b/src/index.ts index de02784..d04a741 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -export { DidServiceWebLac } from './services/did-lac/did.service'; +export { DidServiceWebLac } from './services/did-lac/didWebLac.service'; export { didWelLacAttributes, DidLacService diff --git a/src/services/did-lac/did.service.ts b/src/services/did-lac/did.service.ts index 7638db0..b5d5325 100644 --- a/src/services/did-lac/did.service.ts +++ b/src/services/did-lac/did.service.ts @@ -7,7 +7,6 @@ import { CHAIN_ID, getChainId, log4TSProvider, - resolveDidDomainName, resolveDidRegistryAddress, getRpcUrl, getNodeAddress @@ -32,7 +31,7 @@ import { encode } from 'cbor'; import { VM_RELATIONS } from '../../constants/did-web/lac/didVerificationMethodParams'; @Service() -export class DidServiceWebLac implements DidLacService { +export abstract class DidService implements DidLacService { private readonly didRepository = getRepository(Did); private readonly base58 = require('base-x')( '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' @@ -42,14 +41,13 @@ export class DidServiceWebLac implements DidLacService { private readonly didEncodingVersion = '0001'; // constant for encoding // eslint-disable-next-line max-len private readonly didType = '0001'; // constant - private readonly domainName: string; private readonly chainId: string; private readonly didRegistryAddress: string; private readonly rpcUrl: string; private readonly nodeAddress: string; - private readonly didLacWebIdentifier: string; + private readonly didIdentifier: string; private didRegistryContractInterface: DIDRegistryContractInterface; @@ -57,12 +55,11 @@ export class DidServiceWebLac implements DidLacService { log = log4TSProvider.getLogger('didService'); private keyManagerService: KeyManagerService; - constructor() { + constructor(didIdentifier: string) { this.keyManagerService = new KeyManagerService(); this.chainId = getChainId(); this.didRegistryAddress = resolveDidRegistryAddress(); - this.domainName = resolveDidDomainName(); - this.didLacWebIdentifier = `did:web:${this.domainName}:`; + this.didIdentifier = didIdentifier; this.rpcUrl = getRpcUrl(); this.nodeAddress = getNodeAddress(); this.didRegistryContractInterface = new DIDRegistryContractInterface( @@ -188,7 +185,7 @@ export class DidServiceWebLac implements DidLacService { const did = EntityMapper.mapTo(Did, {}); did.keyId = key.keyId; did.did = - this.didLacWebIdentifier + + this.didIdentifier + this.encode( this.didType, this.chainId, @@ -200,7 +197,7 @@ export class DidServiceWebLac implements DidLacService { } decodeDid(did: string): didWelLacAttributes { - const trimmed = did.replace(this.didLacWebIdentifier, ''); + const trimmed = did.replace(this.didIdentifier, ''); const data = Buffer.from(this.base58.decode(trimmed)); const len = data.length; const encodedPayload = data.subarray(0, len - 4); diff --git a/src/services/did-lac/didLac1.service.ts b/src/services/did-lac/didLac1.service.ts new file mode 100644 index 0000000..cf78d24 --- /dev/null +++ b/src/services/did-lac/didLac1.service.ts @@ -0,0 +1,9 @@ +import { Service } from 'typedi'; +import { DidService } from './did.service'; + +@Service() +export class DidLac1Service extends DidService { + constructor() { + super('lac1:'); + } +} diff --git a/src/services/did-lac/didWebLac.service.ts b/src/services/did-lac/didWebLac.service.ts new file mode 100644 index 0000000..b937a9b --- /dev/null +++ b/src/services/did-lac/didWebLac.service.ts @@ -0,0 +1,11 @@ +import { Service } from 'typedi'; +import { log4TSProvider, resolveDidDomainName } from '../../config'; +import { DidService } from './did.service'; + +@Service() +export class DidServiceWebLac extends DidService { + log = log4TSProvider.getLogger('didService'); + constructor() { + super(`did:web:${resolveDidDomainName()}:`); + } +} From 73c7a565a0d3b873686c5eec5d161f0aec2cdc1a Mon Sep 17 00:00:00 2001 From: eum602 Date: Fri, 26 May 2023 00:10:33 -0500 Subject: [PATCH 21/22] fix: add missing file for docker image building --- Dockerfile.dev | 1 + Dockerfile.prod | 1 + 2 files changed, 2 insertions(+) diff --git a/Dockerfile.dev b/Dockerfile.dev index 1b72d47..e32a8d8 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -2,6 +2,7 @@ FROM node:16.20.0-alpine WORKDIR /app COPY ./package.json . +COPY ./yarn.lock . COPY tsconfig* ./ COPY ormconfig* ./ COPY src ./src diff --git a/Dockerfile.prod b/Dockerfile.prod index f5b6e9e..ca15e1d 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -9,6 +9,7 @@ FROM node:16.20.0-alpine AS production WORKDIR /app COPY --from=builder ./app/dist ./dist COPY package* ./ +COPY ./yarn.lock . COPY tsconfig* ./ COPY prod-paths* ./ COPY ormconfig* ./ From 0adab43649c31123cddf9f95a4cd215ce62eeb6f Mon Sep 17 00:00:00 2001 From: eum602 Date: Fri, 26 May 2023 00:14:57 -0500 Subject: [PATCH 22/22] refactor: rename variable names --- src/index.ts | 3 ++- src/services/did-lac/did.service.ts | 7 ++----- src/services/did-lac/interfaces/did-lac.service.ts | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/index.ts b/src/index.ts index d04a741..bd101b0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ export { DidServiceWebLac } from './services/did-lac/didWebLac.service'; +export { DidLac1Service } from './services/did-lac/didLac1.service'; export { - didWelLacAttributes, + didLacAttributes, DidLacService } from './services/did-lac/interfaces/did-lac.service'; export { DidService, did as DidType } from './services/interfaces/did.service'; diff --git a/src/services/did-lac/did.service.ts b/src/services/did-lac/did.service.ts index b5d5325..2ea4b49 100644 --- a/src/services/did-lac/did.service.ts +++ b/src/services/did-lac/did.service.ts @@ -14,10 +14,7 @@ import { import { Interface, keccak256, toUtf8Bytes } from 'ethers/lib/utils'; import DIDRegistryContractInterface from './did-registry'; import { BadRequestError } from 'routing-controllers'; -import { - DidLacService, - didWelLacAttributes -} from './interfaces/did-lac.service'; +import { DidLacService, didLacAttributes } from './interfaces/did-lac.service'; import { ErrorsMessages } from '../../constants/errorMessages'; import { IJwkAttribute, @@ -196,7 +193,7 @@ export abstract class DidService implements DidLacService { return { did: did.did }; } - decodeDid(did: string): didWelLacAttributes { + decodeDid(did: string): didLacAttributes { const trimmed = did.replace(this.didIdentifier, ''); const data = Buffer.from(this.base58.decode(trimmed)); const len = data.length; diff --git a/src/services/did-lac/interfaces/did-lac.service.ts b/src/services/did-lac/interfaces/did-lac.service.ts index 30aa3da..895ee5c 100644 --- a/src/services/did-lac/interfaces/did-lac.service.ts +++ b/src/services/did-lac/interfaces/did-lac.service.ts @@ -4,7 +4,7 @@ import { } from 'src/interfaces/did-web-lac/did-web-lac.interface'; import { DidService } from '../../interfaces/did.service'; -export type didWelLacAttributes = { +export type didLacAttributes = { address: string; didRegistryAddress: string; chainId: string; @@ -14,5 +14,5 @@ export interface DidLacService extends DidService { addRsaJwkAttribute(jwkRsaAttribute: IJwkRsaAttribute): Promise; addEcJwkAttribute(ecJwkAttribute: IJwkEcAttribute): Promise; getController(did: string): Promise; - decodeDid(did: string): didWelLacAttributes; + decodeDid(did: string): didLacAttributes; }