diff --git a/.example.env b/.example.env index c69f6e3..f323f81 100644 --- a/.example.env +++ b/.example.env @@ -1,6 +1,7 @@ #PORT -PORT = 3000 +PORT = 80 # use port 80 when running with docker +EXPOSED_CONTAINER_SERVER_PORT = 3001 #DOCS @@ -12,17 +13,19 @@ TYPEORM_TYPE = postgres TYPEORM_HOST = localhost TYPEORM_USERNAME = postgres TYPEORM_PASSWORD = postgres -TYPEORM_DATABASE = express_api_base_development +TYPEORM_DATABASE = laccpass_identity 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 @@ -49,3 +52,22 @@ SENDGRID_API_KEY= #Email EMAIL_TRANSPORTER = AWS + +# 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 +SECP256K1_SIGN_ETHEREUM_TRANSACTION=/secp256k1/sign/ethereum-tx +SECP256K1_SIGN_LACCHAIN_TRANSACTION=/secp256k1/sign/lacchain-tx + +# 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 + +# 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/.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/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* ./ 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 4246ed3..4ecf13e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,20 +3,29 @@ services: postgres: image: postgres:latest ports: - - '${TYPEORM_PORT}:${TYPEORM_PORT}' + - '${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 + - type: bind + source: ./pg-prod-db + target: /var/lib/postgresql/data + volume: + nocopy: true + networks: + - backend redis: image: redis:latest command: redis-server --requirepass ${REDIS_PASSWORD} ports: - - '${REDIS_PORT}:${REDIS_PORT}' + - '${EXPOSED_CONTAINER_REDIS_PORT}:${REDIS_PORT}' + networks: + - backend node-api-base: build: context: ./ - dockerfile: Dockerfile.dev + dockerfile: Dockerfile.prod environment: PORT: "${PORT}" JWT_SECRET: "${JWT_SECRET}" @@ -33,7 +42,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: @@ -43,5 +52,10 @@ services: - postgres - redis volumes: - - "./src:/app/src" - - /src/node_modules \ No newline at end of file + - ".env.prod:/app/.env.prod" + networks: + - backend +networks: + backend: + name: backend + external: 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; diff --git a/docs/tech/configuration.md b/docs/tech/configuration.md index fff25e5..96afcb5 100644 --- a/docs/tech/configuration.md +++ b/docs/tech/configuration.md @@ -65,11 +65,36 @@ 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-compose --env-file .env.dev build +docker network create backend ``` ``` -docker-compose --env-file .env.dev up +docker-compose -f docker-compose-dev.yml --env-file .env.dev build +``` + +``` +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 +``` + +``` +mkdir pg-prod-db +``` + +``` +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 201050e..1d3e98a 100644 --- a/ormconfig.ts +++ b/ormconfig.ts @@ -9,9 +9,15 @@ import { TYPEORM_LOGGING, TYPEORM_MIGRATIONS_RUN, PRODUCTION_ENV, - TYPEORM_TYPE + TYPEORM_TYPE, + IS_DEPENDENT_SERVICE, + log4TSProvider } from '@config'; +import { Secp256k1, Secp256k1DbService } from 'lacpass-key-manager'; + +const log = log4TSProvider.getLogger('ormConfig'); + const config: ConnectionOptions = { type: TYPEORM_TYPE as 'mysql' | 'postgres' | 'mongodb', host: TYPEORM_HOST, @@ -20,7 +26,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 +44,13 @@ const config: ConnectionOptions = { } }; +if (IS_DEPENDENT_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 a31e2a6..a8f3656 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,12 @@ { - "name": "identity", - "version": "0.0.1", + "name": "lacpass-identity", + "version": "0.0.10", "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", @@ -63,20 +68,24 @@ "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", "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", "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", "helmet": "^5.0.2", "jsonwebtoken": "^9.0.0", + "lacpass-key-manager": "^0.0.15", "morgan": "^1.10.0", "multer": "^1.4.4", "nodemailer": "^6.7.3", @@ -92,6 +101,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/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/index.ts b/src/config/index.ts index 6c54ee1..c25b99f 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,18 +1,124 @@ +import { + DEFAULT_DOMAIN_NAME, + DEFAULT_REGISTRY, + DOMAIN_NAMES, + REGISTRY +} from '../constants/did-web/lac/didRegistryAddresses'; import { config } from 'dotenv'; +import { ethers } from 'ethers'; +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 if (!process.env.PORT) { console.error('==> Please check 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 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'; 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'; +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( + '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; +}; + +export 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, @@ -39,5 +145,10 @@ export const { AWS_SES_API_VERSION, DOCS_ENABLED, SENDGRID_API_USER, - SENDGRID_API_KEY + SENDGRID_API_KEY, + KEY_MANAGER_BASE_URL, + SECP256K1_KEY, + IS_DEPENDENT_SERVICE, + SECP256K1_SIGN_ETHEREUM_TRANSACTION, + SECP256K1_SIGN_LACCHAIN_TRANSACTION } = process.env; diff --git a/src/constants/did-web/lac/didRegistryAddresses.ts b/src/constants/did-web/lac/didRegistryAddresses.ts new file mode 100644 index 0000000..c8988e6 --- /dev/null +++ b/src/constants/did-web/lac/didRegistryAddresses.ts @@ -0,0 +1,6 @@ +export const REGISTRY: Map = new Map(); +REGISTRY.set('0x9e55c', ['0xA850839d7b37B8e18a9673213CC0f7400dDd8771']); +export const DEFAULT_REGISTRY: Map = new Map(); +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/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/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 5fd5878..ee2c862 100644 --- a/src/constants/errorMessages.ts +++ b/src/constants/errorMessages.ts @@ -18,7 +18,14 @@ 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', + // 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', + 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-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 new file mode 100644 index 0000000..5c2e79f --- /dev/null +++ b/src/controllers/did-web-lac/attribute.controller.ts @@ -0,0 +1,45 @@ +import { + JsonController, + Post, + BadRequestError, + Body +} from 'routing-controllers'; +import { Service } from 'typedi'; +import { DidServiceWebLac } from '../../services/did-lac/didWebLac.service'; +import { ErrorsMessages } from '../../constants/errorMessages'; +import { + EcJwkAttributeDTO, + RsaJwkAttributeDTO +} from '../../dto/did-web-lac/addAttributeDTO'; + +@JsonController('/did-web-lac/attribute') +@Service() +export class DidWebLacAttributeController { + constructor(private readonly didServiceWebLac: DidServiceWebLac) {} + + @Post('/add/jwk/rsa') + async addRsaJwkAttribute( + @Body({ validate: true }) attribute: RsaJwkAttributeDTO + ): Promise { + try { + 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/did-web-lac/did.web.lac.controller.ts b/src/controllers/did-web-lac/did.web.lac.controller.ts new file mode 100644 index 0000000..c2f336b --- /dev/null +++ b/src/controllers/did-web-lac/did.web.lac.controller.ts @@ -0,0 +1,35 @@ +import { + JsonController, + Post, + BadRequestError, + Get, + Param +} from 'routing-controllers'; +import { Service } from 'typedi'; +import { DidServiceWebLac } from '@services/did-lac/didWebLac.service'; +import { ErrorsMessages } from '@constants/errorMessages'; + +@JsonController('/did-web-lac') +@Service() +export class DidWebLacController { + constructor(private readonly didServiceWebLac: DidServiceWebLac) {} + + @Post() + async createDidWebLac(): Promise { + try { + return this.didServiceWebLac.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.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 201f417..60ef9da 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1 +1,6 @@ -export const controllers = []; +// 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 = [DidLac1Controller, DidLac1AttributeController]; diff --git a/src/dto/did-web-lac/addAttributeDTO.ts b/src/dto/did-web-lac/addAttributeDTO.ts new file mode 100644 index 0000000..cc3d18b --- /dev/null +++ b/src/dto/did-web-lac/addAttributeDTO.ts @@ -0,0 +1,43 @@ +import { IsNumber, IsObject, IsString } from 'class-validator'; + +export class RsaJwkDTO { + @IsString() + kty!: string; + @IsString() + e!: string; + @IsString() + n!: string; +} + +export class EcJwkDTO { + @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!: RsaJwkDTO; +} + +export class EcJwkAttributeDTO { + @IsString() + did!: string; + @IsNumber() + validDays!: number; + @IsString() + relation!: string; + @IsObject() + ecJwk!: EcJwkDTO; +} 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/index.ts b/src/index.ts new file mode 100644 index 0000000..bd101b0 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,11 @@ +export { DidServiceWebLac } from './services/did-lac/didWebLac.service'; +export { DidLac1Service } from './services/did-lac/didLac1.service'; +export { + didLacAttributes, + 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 new file mode 100644 index 0000000..c1d91b6 --- /dev/null +++ b/src/interfaces/did-web-lac/did-web-lac.interface.ts @@ -0,0 +1,31 @@ +export interface IJwkAttribute { + did: string; + jwk: any; + validDays: number; + relation: string; +} +export interface IJwkRsaAttribute { + did: string; + rsaJwk: RsaJwk; + validDays: number; + relation: string; +} + +export interface RsaJwk { + kty: string; + 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/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/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-lac/did-registry.ts b/src/services/did-lac/did-registry.ts new file mode 100644 index 0000000..016c0ce --- /dev/null +++ b/src/services/did-lac/did-registry.ts @@ -0,0 +1,41 @@ +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'; +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 = Wallet.createRandom().privateKey; + } + 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 = Math.floor(Date.now() / 1000) + 86400 * 1080 + ): 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..2ea4b49 --- /dev/null +++ b/src/services/did-lac/did.service.ts @@ -0,0 +1,273 @@ +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, + resolveDidRegistryAddress, + getRpcUrl, + getNodeAddress +} from '../../config'; +import { Interface, keccak256, toUtf8Bytes } from 'ethers/lib/utils'; +import DIDRegistryContractInterface from './did-registry'; +import { BadRequestError } from 'routing-controllers'; +import { DidLacService, didLacAttributes } from './interfaces/did-lac.service'; +import { ErrorsMessages } from '../../constants/errorMessages'; +import { + IJwkAttribute, + IJwkEcAttribute, + IJwkRsaAttribute +} 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'; + +@Service() +export abstract class DidService 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 chainId: string; + private readonly didRegistryAddress: string; + private readonly rpcUrl: string; + private readonly nodeAddress: string; + + private readonly didIdentifier: string; + + private didRegistryContractInterface: DIDRegistryContractInterface; + + private readonly lacchainLib: LacchainLib; + + log = log4TSProvider.getLogger('didService'); + private keyManagerService: KeyManagerService; + constructor(didIdentifier: string) { + this.keyManagerService = new KeyManagerService(); + this.chainId = getChainId(); + this.didRegistryAddress = resolveDidRegistryAddress(); + this.didIdentifier = didIdentifier; + this.rpcUrl = getRpcUrl(); + this.nodeAddress = getNodeAddress(); + this.didRegistryContractInterface = new DIDRegistryContractInterface( + this.didRegistryAddress, // base did registry + this.rpcUrl, + undefined, + this.nodeAddress + ); + // TODO: factor providers in such way that did service is independent + this.lacchainLib = new LacchainLib(this.nodeAddress, this.rpcUrl); + } + async addRsaJwkAttribute(jwkRsaAttribute: IJwkRsaAttribute): 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( + jwkAttribute.did + ); + if (chainId.toLowerCase() !== CHAIN_ID.toLowerCase()) { + const message = ErrorsMessages.UNSUPPORTED_CHAIN_ID; + this.log.info(message); + throw new BadRequestError(message); + } + 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 * validDays; + const setAttributeMethodSignature = [ + `function ${methodName}(address,bytes,bytes,uint256) public` + ]; + const setAttributeInterface = new Interface(setAttributeMethodSignature); + const encodedData = setAttributeInterface.encodeFunctionData(methodName, [ + address, + toUtf8Bytes(name), + 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.'); + } + + 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.didIdentifier + + this.encode( + this.didType, + this.chainId, + key.address, + this.didRegistryAddress + ); + await this.didRepository.insert(did); + return { did: did.did }; + } + + decodeDid(did: string): didLacAttributes { + 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); + 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 = 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); + } + 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/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()}:`); + } +} 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..895ee5c --- /dev/null +++ b/src/services/did-lac/interfaces/did-lac.service.ts @@ -0,0 +1,18 @@ +import { + IJwkEcAttribute, + IJwkRsaAttribute +} from 'src/interfaces/did-web-lac/did-web-lac.interface'; +import { DidService } from '../../interfaces/did.service'; + +export type didLacAttributes = { + 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): didLacAttributes; +} 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 new file mode 100644 index 0000000..87ca869 --- /dev/null +++ b/src/services/external/key-manager.service.ts @@ -0,0 +1,140 @@ +import { Service } from 'typedi'; +import fetch from 'node-fetch'; +import { + IS_DEPENDENT_SERVICE, + KEY_MANAGER_BASE_URL, + SECP256K1_KEY, + SECP256K1_SIGN_ETHEREUM_TRANSACTION, + SECP256K1_SIGN_LACCHAIN_TRANSACTION, + log4TSProvider +} from '@config'; +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') { + this.log.info('Configuring key-manager library usage'); + 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 { + 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; + } + + 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/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/**/*" + ] } diff --git a/yarn.lock b/yarn.lock index b0990ed..c45973e 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" @@ -1485,6 +1841,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" @@ -1525,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" @@ -1621,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" @@ -1756,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" @@ -2555,6 +2943,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" @@ -2821,6 +3222,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" @@ -3509,6 +3946,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" @@ -3524,6 +3969,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" @@ -3716,7 +4170,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== @@ -4453,6 +4907,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" @@ -4758,6 +5217,45 @@ koa@^2.8.2: type-is "^1.6.16" vary "^1.1.2" +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" + 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" + typescript-logging "^2.1.0" + typescript-logging-log4ts-style "^2.1.0" + latest-version@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" @@ -5009,6 +5507,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" @@ -5252,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" @@ -6293,6 +6806,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" @@ -7177,6 +7695,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" @@ -7522,6 +8050,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"