diff --git a/Demo Artifacts/iREC/Policies/IRec Policy 8.1 MBP (1670329794.680515003).policy b/Demo Artifacts/iREC/Policies/IRec Policy 8.1 MBP (1670329794.680515003).policy new file mode 100644 index 0000000000..86ff2f4855 Binary files /dev/null and b/Demo Artifacts/iREC/Policies/IRec Policy 8.1 MBP (1670329794.680515003).policy differ diff --git a/Demo Artifacts/iREC/Policies/IRec Policy 8.2 MBP (1670500065.430227921).policy b/Demo Artifacts/iREC/Policies/IRec Policy 8.2 MBP (1670500065.430227921).policy new file mode 100644 index 0000000000..041aca8266 Binary files /dev/null and b/Demo Artifacts/iREC/Policies/IRec Policy 8.2 MBP (1670500065.430227921).policy differ diff --git a/Demo Artifacts/iREC/readme.md b/Demo Artifacts/iREC/readme.md index 864ea9a93d..43c5370ad1 100644 --- a/Demo Artifacts/iREC/readme.md +++ b/Demo Artifacts/iREC/readme.md @@ -25,8 +25,10 @@ As the initial step in IREC Policy involve creation of following Schemas: iRec r | iREC 3 | 1662641840.731000003 | iRec 2 Policy + revoke actions (Added: Revoke Block, Button Block) | [Link](https://github.com/hashgraph/guardian/blob/main/Demo%20Artifacts/iREC/Policies/IRec%20Policy%203.policy) | | iREC 4 | 1662642008.325450377 | iRec 3 Policy + auto associate and grant KYC token actions (Added: Token Action Block, Token Confirmation Block) | [Link](https://github.com/hashgraph/guardian/blob/main/Demo%20Artifacts/iREC/Policies/IRec%20Policy%204.policy) | | iREC policy 5 group | 1663850151.496004277 | Demonstrates the usage of the new concepts of common groups and multi-signature approvals. Document approval responsibilities are given to the new 'Approver' role, which belongs to a group. Users can become members of the group without the invite, and thereby become approvers. | [Link](https://github.com/hashgraph/guardian/blob/main/Demo%20Artifacts/iREC/Policies/IRec%20Policy%205%20group%20(1663850151.496004277).policy) | -| IRec Policy 6 search documents | 1666182325.863957003 | The ability to process other than input documents in calculation block (calculateMathVariables block improvement) | [Link](https://github.com/hashgraph/guardian/blob/develop/Demo%20Artifacts/iREC/Policies/IRec%20Policy%206%20search%20documents%20(1666182325.863957003).policy) | -| IRec Policy 7 split documents | 1666798058.496271367 | Splitting documents to chunks (added splitBlock) | [Link](https://github.com/hashgraph/guardian/blob/develop/Demo%20Artifacts/iREC/Policies/IRec%20Policy%207%20split%20documents%20(1666798058.496271367).policy) | +| IRec Policy 6 search documents | 1666182325.863957003 | The ability to process other than input documents in calculation block (calculateMathVariables block improvement) | [Link](https://github.com/hashgraph/guardian/blob/main/Demo%20Artifacts/iREC/Policies/IRec%20Policy%206%20search%20documents%20(1666182325.863957003).policy) | +| IRec Policy 7 split documents | 1666798058.496271367 | Splitting documents to chunks (added splitBlock) | [Link](https://github.com/hashgraph/guardian/blob/main/Demo%20Artifacts/iREC/Policies/IRec%20Policy%207%20split%20documents%20(1666798058.496271367).policy) | +| IRec Policy 8.1 MBP | 1670329794.680515003 | Policy based on iREC Policy 4 where there are two impacts in one mint block | [Link](https://github.com/hashgraph/guardian/blob/main/Demo%20Artifacts/iREC/Policies/IRec%20Policy%208.1%20MBP%20(1670329794.680515003).policy) | +| IRec Policy 8.2 MBP | 1670500065.430227921 | Policy based on iREC Policy 4 where the scenario is that there are two impacts in two different mint blocks | [Link](https://github.com/hashgraph/guardian/blob/main/Demo%20Artifacts/iREC/Policies/IRec%20Policy%208.2%20MBP%20(1670500065.430227921).policy) | For complete User Guide and API Flow for executing IREC Policy, please refer to : diff --git a/api-docs/api/swagger/swagger.yaml b/api-docs/api/swagger/swagger.yaml index 27fe9ec95b..51f49e6e02 100644 --- a/api-docs/api/swagger/swagger.yaml +++ b/api-docs/api/swagger/swagger.yaml @@ -2,7 +2,7 @@ openapi: 3.0.0 info: title: "Guardian" description: "The Guardian is a modular open-source solution that includes best-in-class identity management and decentralized ledger technology (DLT) libraries. At the heart of the Guardian solution is a sophisticated Policy Workflow Engine (PWE) that enables applications to offer a requirements-based tokenization implementation." - version: "2.7.0" + version: "2.8.0" contact: name: "API developer" url: "https://envisionblockchain.com" @@ -612,6 +612,38 @@ components: type: string user: type: string + Contract: + type: object + properties: + id: + type: string + contractId: + type: string + description: + type: string + owner: + type: string + isOwnerCreator: + type: string + status: + type: string + RetireRequest: + type: object + properties: + id: + type: string + contractId: + type: string + baseTokenId: + type: string + owner: + type: string + oppositeTokenId: + type: string + baseTokenCount: + type: number + oppositeTokenCount: + type: number tags: - name: "accounts" description: "Operations related to Guardian users" @@ -641,6 +673,8 @@ tags: description: "Task Status" - name: "artifacts" description: "Policy Artifacts" + - name: "contracts" + description: "Contracts" paths: /accounts: get: @@ -4595,3 +4629,484 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + + /contracts: + get: + tags: + - contracts + description: Returns all contracts. + security: + - bearerAuth: [] + summary: Returns all contracts. + parameters: + - in: query + name: pageIndex + schema: + type: integer + description: The number of pages to skip before starting to collect the result set + examples: + pageIndex: + summary: Example of a pageIndex + value: 0 + - in: query + name: pageSize + schema: + type: integer + description: The numbers of items to return + examples: + pageSize: + summary: Example of a pageSize + value: 100 + responses: + 200: + description: Successful operation. + headers: + x-total-count: + schema: + type: integer + description: Total items in the collection. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Contract' + 401: + description: Unauthorized. + 403: + description: Forbidden. + 500: + description: Internal server error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: + tags: + - contracts + description: Creates new contract. Only users with the Standard Registry role are allowed to make the request. + security: + - bearerAuth: [] + summary: Create new contract. + requestBody: + description: Request Object Parameters. + content: + application/json: + schema: + type: object + properties: + description: + type: string + responses: + 200: + description: Successful operation. + content: + application/json: + schema: + $ref: "#/components/schemas/Contract" + 401: + description: Unauthorized. + 403: + description: Forbidden. + 500: + description: Internal server error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /contracts/import: + post: + tags: + - contracts + description: Creates new contract. Only users with the Standard Registry role are allowed to make the request. + security: + - bearerAuth: [] + summary: Import new contract. + requestBody: + description: Request Object Parameters. + content: + application/json: + schema: + type: object + properties: + contractId: + type: string + description: + type: string + responses: + 200: + description: Successful operation. + content: + application/json: + schema: + $ref: '#/components/schemas/Contract' + 401: + description: Unauthorized. + 403: + description: Forbidden. + 500: + description: Internal server error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /contracts/{contractId}/user: + post: + tags: + - contracts + description: Add new contract user. Only users with the Standard Registry role are allowed to make the request. + security: + - bearerAuth: [] + summary: Add new contract user. + parameters: + - in: path + name: contractId + schema: + type: string + description: Contract identifier + required: true + requestBody: + description: Request Object Parameters. + content: + application/json: + schema: + type: object + properties: + userId: + type: string + responses: + 200: + description: Successful operation. + content: + application/json: + schema: + type: boolean + 401: + description: Unauthorized. + 403: + description: Forbidden. + 500: + description: Internal server error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /contracts/{contractId}/status: + post: + tags: + - contracts + description: Update contract status. Only users with the Standard Registry role are allowed to make the request. + security: + - bearerAuth: [] + summary: Update contract status. + parameters: + - in: path + name: contractId + schema: + type: string + description: Contract identifier + required: true + responses: + 200: + description: Successful operation. + content: + application/json: + schema: + type: boolean + 401: + description: Unauthorized. + 403: + description: Forbidden. + 500: + description: Internal server error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /contracts/{contractId}/pair: + post: + tags: + - contracts + description: Creates new contract pair. Only users with the Standard Registry role are allowed to make the request. + security: + - bearerAuth: [] + summary: Creates new contract pair. + parameters: + - in: path + name: contractId + schema: + type: string + description: Contract identifier + required: true + requestBody: + description: Request Object Parameters. + content: + application/json: + schema: + type: object + properties: + baseTokenId: + type: string + oppositeTokenId: + type: string + baseTokenCount: + type: string + oppositeTokenCount: + type: string + responses: + 200: + description: Successful operation. + content: + application/json: + schema: + type: boolean + 401: + description: Unauthorized. + 403: + description: Forbidden. + 500: + description: Internal server error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /contracts/pair: + get: + tags: + - contracts + description: Returns all contracts pairs. + security: + - bearerAuth: [] + summary: Returns all contracts pairs. + parameters: + - in: query + name: pageIndex + schema: + type: integer + description: The number of pages to skip before starting to collect the result set + examples: + pageIndex: + summary: Example of a pageIndex + value: 0 + - in: query + name: pageSize + schema: + type: integer + description: The numbers of items to return + examples: + pageSize: + summary: Example of a pageSize + value: 100 + responses: + 200: + description: Successful operation. + headers: + x-total-count: + schema: + type: integer + description: Total items in the collection. + content: + application/json: + schema: + type: array + items: + type: object + properties: + baseTokenRate: + type: string + oppositeTokenRate: + type: string + contractId: + type: string + description: + type: string + 401: + description: Unauthorized. + 403: + description: Forbidden. + 500: + description: Internal server error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /contracts/{contractId}/retire/request: + post: + tags: + - contracts + description: Creates new contract retire request. + security: + - bearerAuth: [] + summary: Creates new contract retire request. + parameters: + - in: path + name: contractId + schema: + type: string + description: Contract identifier + required: true + requestBody: + description: Request Object Parameters. + content: + application/json: + schema: + type: object + properties: + baseTokenId: + type: string + oppositeTokenId: + type: string + baseTokenCount: + type: string + oppositeTokenCount: + type: string + baseTokenSerials: + type: string + oppositeTokenSerials: + type: string + responses: + 200: + description: Successful operation. + content: + application/json: + schema: + $ref: "#/components/schemas/RetireRequest" + 401: + description: Unauthorized. + 403: + description: Forbidden. + 500: + description: Internal server error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /contracts/retire/request: + get: + tags: + - contracts + description: Returns all contract requests. + security: + - bearerAuth: [] + summary: Returns all contract requests. + parameters: + - in: query + name: contractId + schema: + type: string + description: Contract Identifier + - in: query + name: pageIndex + schema: + type: integer + description: The number of pages to skip before starting to collect the result set + examples: + pageIndex: + summary: Example of a pageIndex + value: 0 + - in: query + name: pageSize + schema: + type: integer + description: The numbers of items to return + examples: + pageSize: + summary: Example of a pageSize + value: 100 + responses: + 200: + description: Successful operation. + headers: + x-total-count: + schema: + type: integer + description: Total items in the collection. + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/RetireRequest" + 401: + description: Unauthorized. + 403: + description: Forbidden. + 500: + description: Internal server error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - contracts + description: Cancel contract requests. + security: + - bearerAuth: [] + summary: Cancel contract requests. + parameters: + - in: query + name: requestId + schema: + type: string + description: Request Identifier + required: true + responses: + 200: + description: Successful operation. + content: + application/json: + schema: + type: boolean + 401: + description: Unauthorized. + 403: + description: Forbidden. + 500: + description: Internal server error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /contracts/retire: + post: + tags: + - contracts + description: Retire tokens. Only users with the Standard Registry role are allowed to make the request. + security: + - bearerAuth: [] + summary: Retire tokens. + requestBody: + description: Request Object Parameters. + content: + application/json: + schema: + type: object + properties: + requestId: + type: string + responses: + 200: + description: Successful operation. + content: + application/json: + schema: + type: boolean + 401: + description: Unauthorized. + 403: + description: Forbidden. + 500: + description: Internal server error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' diff --git a/api-docs/package.json b/api-docs/package.json index b521f6c36e..04ad303b15 100644 --- a/api-docs/package.json +++ b/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "api-docs", - "version": "2.7.0", + "version": "2.8.0-prerelease", "description": "Swagger Documentation", "main": "dist/index.js", "scripts": { @@ -23,5 +23,6 @@ "@types/swagger-ui-express": "^4.1.3", "tslint": "^6.1.3", "typescript": "^4.6.3" - } + }, + "stableVersion": "2.7.0" } diff --git a/api-gateway/.env b/api-gateway/.env index 23396f797c..42d56e4403 100644 --- a/api-gateway/.env +++ b/api-gateway/.env @@ -1,3 +1,5 @@ MQ_ADDRESS="localhost" SERVICE_CHANNEL="api-gateway" MRV_ADDRESS="http://localhost:3003/mrv" +#MQ_MESSAGE_CHUNK=5000000 +#RAW_REQUEST_LIMIT="1gb" diff --git a/api-gateway/.env.docker b/api-gateway/.env.docker index a3a9877fe0..f2e1cb1df7 100644 --- a/api-gateway/.env.docker +++ b/api-gateway/.env.docker @@ -1,3 +1,5 @@ MQ_ADDRESS="message-broker" SERVICE_CHANNEL="api-gateway" MRV_ADDRESS="http://message-broker:3003/mrv" +#MQ_MESSAGE_CHUNK=5000000 +#RAW_REQUEST_LIMIT="1gb" diff --git a/api-gateway/package.json b/api-gateway/package.json index 195ffe7840..7ced8074d8 100644 --- a/api-gateway/package.json +++ b/api-gateway/package.json @@ -8,8 +8,8 @@ }, "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.7.0", - "@guardian/interfaces": "^2.7.0", + "@guardian/common": "^2.8.0-prerelease", + "@guardian/interfaces": "^2.8.0-prerelease", "@types/express-fileupload": "^1.4.1", "dotenv": "^16.0.0", "express": "^4.17.1", @@ -51,5 +51,6 @@ "start": "node dist/index.js", "test": "mocha tests/**/*.test.js --reporter mocha-junit-reporter --reporter-options mochaFile=../test_results/ui-service.xml" }, - "version": "2.7.0" + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0" } diff --git a/api-gateway/src/api/service/contract.ts b/api-gateway/src/api/service/contract.ts new file mode 100644 index 0000000000..4a132bb25f --- /dev/null +++ b/api-gateway/src/api/service/contract.ts @@ -0,0 +1,238 @@ +import { Guardians } from '@helpers/guardians'; +import { Response, Router } from 'express'; +import { UserRole } from '@guardian/interfaces'; +import { permissionHelper } from '@auth/authorization-helper'; +import { AuthenticatedRequest, Logger } from '@guardian/common'; + +/** + * Contract route + */ +export const contractAPI = Router(); + +contractAPI.get('/', async (req: AuthenticatedRequest, res: Response) => { + try { + const user = req.user; + const guardians = new Guardians(); + const [contracts, count] = await guardians.getContracts( + user.parent || user.did, + req.query.pageIndex as any, + req.query.pageSize as any + ); + res.status(200).setHeader('X-Total-Count', count).json(contracts); + } catch (error) { + new Logger().error(error, ['API_GATEWAY']); + res.status(500).json({ code: error.code, message: error.message }); + } +}); + +contractAPI.post( + '/', + permissionHelper(UserRole.STANDARD_REGISTRY), + async (req: AuthenticatedRequest, res: Response) => { + try { + const user = req.user; + const { description } = req.body; + const guardians = new Guardians(); + res.status(200).json( + await guardians.createContract(user.did, description) + ); + } catch (error) { + new Logger().error(error, ['API_GATEWAY']); + res.status(500).json({ code: 500, message: error.message }); + } + } +); + +contractAPI.post( + '/import', + permissionHelper(UserRole.STANDARD_REGISTRY), + async (req: AuthenticatedRequest, res: Response) => { + try { + const user = req.user; + const { contractId, description } = req.body; + const guardians = new Guardians(); + res.status(200).json( + await guardians.importContract( + user.did, + contractId, + description + ) + ); + } catch (error) { + new Logger().error(error, ['API_GATEWAY']); + res.status(500).json({ code: 500, message: error.message }); + } + } +); + +contractAPI.post( + '/:contractId/user', + permissionHelper(UserRole.STANDARD_REGISTRY), + async (req: AuthenticatedRequest, res: Response) => { + try { + const user = req.user; + const { userId } = req.body; + const guardians = new Guardians(); + res.status(200).json( + await guardians.addUser(user.did, userId, req.params.contractId) + ); + } catch (error) { + new Logger().error(error, ['API_GATEWAY']); + res.status(500).json({ code: 500, message: error.message }); + } + } +); + +contractAPI.post( + '/:contractId/status', + permissionHelper(UserRole.STANDARD_REGISTRY), + async (req: AuthenticatedRequest, res: Response) => { + try { + const user = req.user; + const guardians = new Guardians(); + res.status(200).json( + await guardians.updateStatus(user.did, req.params.contractId) + ); + } catch (error) { + new Logger().error(error, ['API_GATEWAY']); + res.status(500).json({ code: 500, message: error.message }); + } + } +); + +contractAPI.get('/pair', async (req: AuthenticatedRequest, res: Response) => { + try { + const user = req.user; + const guardians = new Guardians(); + res.status(200).json( + await guardians.getContractPair( + user.did, + user.parent || user.did, + req.query?.baseTokenId as string, + req.query?.oppositeTokenId as string + ) + ); + } catch (error) { + new Logger().error(error, ['API_GATEWAY']); + res.status(500).json({ code: 500, message: error.message }); + } +}); + +contractAPI.post( + '/:contractId/pair', + permissionHelper(UserRole.STANDARD_REGISTRY), + async (req: AuthenticatedRequest, res: Response) => { + try { + const user = req.user; + const { + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount, + } = req.body; + const guardians = new Guardians(); + res.status(200).json( + await guardians.addContractPair( + user.did, + req.params.contractId, + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount + ) + ); + } catch (error) { + new Logger().error(error, ['API_GATEWAY']); + res.status(500).json({ code: 500, message: error.message }); + } + } +); + +contractAPI.get( + '/retire/request', + async (req: AuthenticatedRequest, res: Response) => { + try { + const user = req.user; + const guardians = new Guardians(); + const [requests, count] = await guardians.getRetireRequests( + user.parent || user.did, + user.role === UserRole.USER ? user.did : null, + req.query?.contractId as string, + req.query?.pageIndex as any, + req.query?.pageSize as any + ); + res.status(200).setHeader('X-Total-Count', count).json(requests); + } catch (error) { + new Logger().error(error, ['API_GATEWAY']); + res.status(500).json({ code: 500, message: error.message }); + } + } +); + +contractAPI.post( + '/:contractId/retire/request', + async (req: AuthenticatedRequest, res: Response) => { + try { + const user = req.user; + const { + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount, + baseTokenSerials, + oppositeTokenSerials, + } = req.body; + const guardians = new Guardians(); + res.status(200).json( + await guardians.retireRequest( + user.did, + req.params.contractId, + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount, + baseTokenSerials, + oppositeTokenSerials + ) + ); + } catch (error) { + new Logger().error(error, ['API_GATEWAY']); + res.status(500).json({ code: 500, message: error.message }); + } + } +); + +contractAPI.delete( + '/retire/request', + async (req: AuthenticatedRequest, res: Response) => { + try { + const user = req.user; + const guardians = new Guardians(); + res.status(200).json( + await guardians.cancelRetireRequest( + user.did, + req.query?.requestId as string + ) + ); + } catch (error) { + new Logger().error(error, ['API_GATEWAY']); + res.status(500).json({ code: 500, message: error.message }); + } + } +); + +contractAPI.post( + '/retire', + permissionHelper(UserRole.STANDARD_REGISTRY), + async (req: AuthenticatedRequest, res: Response) => { + try { + const user = req.user; + const { requestId } = req.body; + const guardians = new Guardians(); + res.status(200).json(await guardians.retire(user.did, requestId)); + } catch (error) { + new Logger().error(error, ['API_GATEWAY']); + res.status(500).json({ code: 500, message: error.message }); + } + } +); diff --git a/api-gateway/src/app.ts b/api-gateway/src/app.ts index e69f365efb..15a4a7c8f2 100644 --- a/api-gateway/src/app.ts +++ b/api-gateway/src/app.ts @@ -26,8 +26,10 @@ import { TaskManager } from '@helpers/task-manager'; import { singleSchemaRoute } from '@api/service/schema'; import { artifactAPI } from '@api/service/artifact'; import fileupload from 'express-fileupload'; +import { contractAPI } from '@api/service/contract'; const PORT = process.env.PORT || 3002; +const RAW_REQUEST_LIMIT = process.env.RAW_REQUEST_LIMIT || '1gb'; Promise.all([ MessageBrokerChannel.connect('API_GATEWAY'), @@ -37,7 +39,7 @@ Promise.all([ app.use(express.json()); app.use(express.raw({ inflate: true, - limit: '1gb', + limit: RAW_REQUEST_LIMIT, type: 'binary/octet-stream' })); app.use(fileupload()); @@ -73,6 +75,7 @@ Promise.all([ app.use('/ipfs', authorizationHelper, ipfsAPI); app.use('/logs', authorizationHelper, loggerAPI); app.use('/tasks/', taskAPI); + app.use('/contracts', authorizationHelper, contractAPI); ///////////////////////////////////////// server.listen(PORT, () => { diff --git a/api-gateway/src/helpers/guardians.ts b/api-gateway/src/helpers/guardians.ts index b6ba5c72a0..cde09d684c 100644 --- a/api-gateway/src/helpers/guardians.ts +++ b/api-gateway/src/helpers/guardians.ts @@ -890,4 +890,231 @@ export class Guardians extends ServiceRequestsBase { cid, responseType }); } + + /** + * Create Contract + * @param did + * @param description + * @returns Created Contract + */ + public async createContract( + did: string, + description: string + ): Promise { + return await this.request(MessageAPI.CREATE_CONTRACT, { + did, + description, + }); + } + + /** + * Import Contract + * @param did + * @param contractId + * @param description + * @returns Imported Contract + */ + public async importContract( + did: string, + contractId: string, + description: string + ): Promise { + return await this.request(MessageAPI.IMPORT_CONTRACT, { + did, + contractId, + description, + }); + } + + /** + * Create Contract + * @param owner + * @param pageIndex + * @param pageSize + * @returns Contracts And Count + */ + public async getContracts( + owner: string, + pageIndex?: any, + pageSize?: any + ): Promise<[any, number]> { + return await this.request(MessageAPI.GET_CONTRACT, { + owner, + pageIndex, + pageSize, + }); + } + + /** + * Add User To Contract + * @param did + * @param userId + * @param contractId + * @returns Operation Success + */ + public async addUser( + did: string, + userId: string, + contractId: string + ): Promise { + return await this.request(MessageAPI.ADD_CONTRACT_USER, { + did, + userId, + contractId, + }); + } + + /** + * Check Contract Status + * @param did + * @param contractId + * @returns Operation Success + */ + public async updateStatus( + did: string, + contractId: string + ): Promise { + return await this.request(MessageAPI.CHECK_CONTRACT_STATUS, { + contractId, + did, + }); + } + + /** + * Add Contract Pair + * @param did + * @param contractId + * @param baseTokenId + * @param oppositeTokenId + * @param baseTokenCount + * @param oppositeTokenCount + * @returns Operation Success + */ + public async addContractPair( + did: string, + contractId: string, + baseTokenId: string, + oppositeTokenId: string, + baseTokenCount: number, + oppositeTokenCount: number + ): Promise { + return await this.request(MessageAPI.ADD_CONTRACT_PAIR, { + did, + contractId, + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount, + }); + } + + /** + * Get Contracts Pairs + * @param did + * @param owner + * @param baseTokenId + * @param oppositeTokenId + * @returns Contracts And Pairs + */ + public async getContractPair( + did: string, + owner: string, + baseTokenId: string, + oppositeTokenId: string + ): Promise { + return await this.request(MessageAPI.GET_CONTRACT_PAIR, { + did, + baseTokenId, + oppositeTokenId, + owner, + }); + } + + /** + * Create Retire Request + * @param did + * @param contractId + * @param baseTokenId + * @param oppositeTokenId + * @param baseTokenCount + * @param oppositeTokenCount + * @param baseTokenSerials + * @param oppositeTokenSerials + * @returns Operation Success + */ + public async retireRequest( + did: string, + contractId: string, + baseTokenId: string, + oppositeTokenId: string, + baseTokenCount: number, + oppositeTokenCount: number, + baseTokenSerials: number[], + oppositeTokenSerials: number[] + ): Promise { + return await this.request(MessageAPI.ADD_RETIRE_REQUEST, { + did, + contractId, + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount, + baseTokenSerials, + oppositeTokenSerials, + }); + } + + /** + * Cancel Retire Request + * @param did + * @param requestId + * @returns Operation Success + */ + public async cancelRetireRequest( + did: string, + requestId: string + ): Promise { + return await this.request(MessageAPI.CANCEL_RETIRE_REQUEST, { + did, + requestId, + }); + } + + /** + * Get Retire Requests + * @param did + * @param owner + * @param contractId + * @param pageIndex + * @param pageSize + * @returns Retire Requests And Count + */ + public async getRetireRequests( + did: string, + owner?: string, + contractId?: string, + pageIndex?: any, + pageSize?: any + ): Promise<[any, number]> { + return await this.request(MessageAPI.GET_RETIRE_REQUEST, { + did, + owner, + contractId, + pageIndex, + pageSize, + }); + } + + /** + * Retire Tokens + * @param did + * @param requestId + * @returns Operation Success + */ + public async retire(did: string, requestId: string): Promise { + return await this.request(MessageAPI.RETIRE_TOKENS, { + did, + requestId, + }); + } } diff --git a/api-tests/package.json b/api-tests/package.json index 313cb71032..5858797a4e 100644 --- a/api-tests/package.json +++ b/api-tests/package.json @@ -1,6 +1,7 @@ { "name": "api-tests", - "version": "2.7.0", + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0", "description": "API Tests", "main": "index.js", "scripts": { diff --git a/auth-service/.env b/auth-service/.env index 4a3eeddecd..cbea7f2df6 100644 --- a/auth-service/.env +++ b/auth-service/.env @@ -10,3 +10,4 @@ HASHICORP_NAMESPACE="admin" HASHICORP_ENCRIPTION_ALG="sha512" #HASHICORP_USEAL_KEY="" #IMPORT_KEYS_FROM_DB=1 +#MQ_MESSAGE_CHUNK=5000000 diff --git a/auth-service/.env.docker b/auth-service/.env.docker index efd68cf756..9898f76721 100644 --- a/auth-service/.env.docker +++ b/auth-service/.env.docker @@ -10,3 +10,4 @@ HASHICORP_NAMESPACE="admin" HASHICORP_ENCRIPTION_ALG="sha512" #HASHICORP_UNSEAL_KEY="" #IMPORT_KEYS_FROM_DB=1 +#MQ_MESSAGE_CHUNK=5000000 diff --git a/auth-service/package.json b/auth-service/package.json index 6508649858..237e7a0a47 100644 --- a/auth-service/package.json +++ b/auth-service/package.json @@ -6,8 +6,8 @@ }, "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.7.0", - "@guardian/interfaces": "^2.7.0", + "@guardian/common": "^2.8.0-prerelease", + "@guardian/interfaces": "^2.8.0-prerelease", "@mikro-orm/core": "~5.3.0", "@mikro-orm/mongodb": "~5.3.0", "dotenv": "^16.0.0", @@ -46,5 +46,6 @@ "start": "node dist/index.js", "test": "mocha tests/**/*.test.js --reporter mocha-junit-reporter --reporter-options mochaFile=../test_results/ui-service.xml" }, - "version": "2.7.0" + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0" } diff --git a/common/package.json b/common/package.json index caa7f9115e..305d121300 100644 --- a/common/package.json +++ b/common/package.json @@ -1,7 +1,7 @@ { "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/interfaces": "^2.7.0", + "@guardian/interfaces": "^2.8.0-prerelease", "@mikro-orm/core": "~5.3.0", "@mikro-orm/migrations-mongodb": "~5.3.0", "@mikro-orm/mongodb": "~5.3.0", @@ -34,5 +34,6 @@ "prepare": "npm run build", "test": "echo \"Error: no test specified\" && exit 1" }, - "version": "2.7.0" + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0" } diff --git a/common/src/mq/message-broker-channel.ts b/common/src/mq/message-broker-channel.ts index ab5250b343..0361ccdf1b 100644 --- a/common/src/mq/message-broker-channel.ts +++ b/common/src/mq/message-broker-channel.ts @@ -5,9 +5,11 @@ import { GenerateUUIDv4 } from '@guardian/interfaces'; const MQ_TIMEOUT = 300000; /** - * Message Chunk Size ~ 1 MB + * Message Chunk Size + * Default value ~ 1 MB */ -const MQ_MESSAGE_CHUNK = 1000000; +const MQ_MESSAGE_CHUNK = + Math.floor(Math.abs(+process.env.MQ_MESSAGE_CHUNK)) || 1000000; const reqMap = new Map(); const chunkMap = new Map(); /** diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index b18dc0ad50..84f658674d 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -1,7 +1,7 @@ version: '3.8' services: mongo: - image: mongo:6 + image: mongo:6.0.3 command: "--setParameter allowDiskUseByDefault=true" restart: always expose: @@ -9,7 +9,7 @@ services: ports: - 27017:27017 ipfs-node: - image: yeasy/ipfs:latest + image: ipfs/kubo:v0.17.0 ports: - "5001:5001" - "5002:5002" @@ -18,7 +18,7 @@ services: - "8080:8080" - "8081:8081" message-broker: - image: nats + image: nats:2.9.8 expose: - 4222 ports: @@ -159,7 +159,7 @@ services: command: npm run dev:docker --prefix /app/frontend web-proxy: - image: nginx + image: nginx:1.23.2 ports: - '3000:80' depends_on: @@ -173,7 +173,7 @@ services: - ./web-proxy/configs/local.conf:/etc/nginx/conf.d/default.conf volumes: mongo: - # volume-guardian-service: - # volume-ui-service: - # volume-mrv-sender: - # volume-message-broker: + # volume-guardian-service: + # volume-ui-service: + # volume-mrv-sender: + # volume-message-broker: diff --git a/docker-compose.yml b/docker-compose.yml index fd14d8a3ba..6ebc199c97 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,14 @@ version: "3.8" services: mongo: - image: mongo:6 + image: mongo:6.0.3 command: "--setParameter allowDiskUseByDefault=true" restart: always expose: - 27017 ipfs-node: - image: yeasy/ipfs:latest + image: ipfs/kubo:v0.17.0 ports: - "5001:5001" - "5002:5002" @@ -25,7 +25,7 @@ services: - 3001 message-broker: - image: nats + image: nats:2.9.8 expose: - 4222 ports: @@ -33,7 +33,7 @@ services: command: '--http_port 8222' vault: - image: vault + image: vault:1.12.2 expose: - 8200 ports: @@ -60,6 +60,7 @@ services: dockerfile: ./worker-service/Dockerfile depends_on: - ipfs-node + - auth-service environment: - SERVICE_CHANNEL="worker.1" @@ -69,6 +70,7 @@ services: dockerfile: ./worker-service/Dockerfile depends_on: - ipfs-node + - auth-service environment: - SERVICE_CHANNEL="worker.2" @@ -135,7 +137,7 @@ services: - mrv-sender volumes: mongo: - # volume-guardian-service: - # volume-ui-service: - # volume-mrv-sender: - # volume-message-broker: + # volume-guardian-service: + # volume-ui-service: + # volume-mrv-sender: + # volume-message-broker: diff --git a/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png b/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png new file mode 100644 index 0000000000..9f1a84da4d Binary files /dev/null and b/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png differ diff --git a/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (2).png b/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (2).png new file mode 100644 index 0000000000..9f1a84da4d Binary files /dev/null and b/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (2).png differ diff --git a/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png b/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png new file mode 100644 index 0000000000..9f1a84da4d Binary files /dev/null and b/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png differ diff --git a/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (2).png b/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (2).png new file mode 100644 index 0000000000..9f1a84da4d Binary files /dev/null and b/docs/.gitbook/assets/API_10 (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (2).png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-12 at 5.00.05 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-12 at 5.00.05 PM.png new file mode 100644 index 0000000000..027aa2056a Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-12 at 5.00.05 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-12 at 5.04.57 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-12 at 5.04.57 PM.png new file mode 100644 index 0000000000..d2ada86a5d Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-12 at 5.04.57 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.22.01 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.22.01 PM.png new file mode 100644 index 0000000000..8e07425c79 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.22.01 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.22.23 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.22.23 PM.png new file mode 100644 index 0000000000..d3fe122ff3 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.22.23 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.22.43 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.22.43 PM.png new file mode 100644 index 0000000000..b79ef974c6 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.22.43 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.23.09 PM (1).png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.23.09 PM (1).png new file mode 100644 index 0000000000..420ca911e7 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.23.09 PM (1).png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.23.09 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.23.09 PM.png new file mode 100644 index 0000000000..420ca911e7 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.23.09 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.23.42 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.23.42 PM.png new file mode 100644 index 0000000000..a81dffdade Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.23.42 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.24.00 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.24.00 PM.png new file mode 100644 index 0000000000..6ed5abee93 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.24.00 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.24.25 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.24.25 PM.png new file mode 100644 index 0000000000..d8bb4c395e Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.24.25 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.24.41 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.24.41 PM.png new file mode 100644 index 0000000000..665c84708d Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.24.41 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.01 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.01 PM.png new file mode 100644 index 0000000000..55c24e8207 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.01 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.12 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.12 PM.png new file mode 100644 index 0000000000..68da0df381 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.12 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.27 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.27 PM.png new file mode 100644 index 0000000000..d05d1f4493 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.27 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.45 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.45 PM.png new file mode 100644 index 0000000000..b1306a5f14 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.45 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.59 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.59 PM.png new file mode 100644 index 0000000000..5489b870dd Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.25.59 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.16 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.16 PM.png new file mode 100644 index 0000000000..c1d22fa500 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.16 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.36 PM (1).png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.36 PM (1).png new file mode 100644 index 0000000000..b6fe08dbc5 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.36 PM (1).png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.36 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.36 PM.png new file mode 100644 index 0000000000..b6fe08dbc5 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.36 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.56 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.56 PM.png new file mode 100644 index 0000000000..f652f4dcfa Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.26.56 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.27.10 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.27.10 PM.png new file mode 100644 index 0000000000..6f92b2c7f0 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.27.10 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.27.29 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.27.29 PM.png new file mode 100644 index 0000000000..b323059912 Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.27.29 PM.png differ diff --git a/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.27.54 PM.png b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.27.54 PM.png new file mode 100644 index 0000000000..3cb695744a Binary files /dev/null and b/docs/.gitbook/assets/Screen Shot 2022-12-14 at 1.27.54 PM.png differ diff --git a/docs/.gitbook/assets/image (1) (3) (2).png b/docs/.gitbook/assets/image (1) (3) (2).png index bfc7de1b26..e40832f472 100644 Binary files a/docs/.gitbook/assets/image (1) (3) (2).png and b/docs/.gitbook/assets/image (1) (3) (2).png differ diff --git a/docs/.gitbook/assets/image (1) (3).png b/docs/.gitbook/assets/image (1) (3).png index e40832f472..24b442b62c 100644 Binary files a/docs/.gitbook/assets/image (1) (3).png and b/docs/.gitbook/assets/image (1) (3).png differ diff --git a/docs/.gitbook/assets/image (1) (7).png b/docs/.gitbook/assets/image (1) (7).png new file mode 100644 index 0000000000..2efcff5829 Binary files /dev/null and b/docs/.gitbook/assets/image (1) (7).png differ diff --git a/docs/.gitbook/assets/image (1) (8).png b/docs/.gitbook/assets/image (1) (8).png new file mode 100644 index 0000000000..01426abd29 Binary files /dev/null and b/docs/.gitbook/assets/image (1) (8).png differ diff --git a/docs/.gitbook/assets/image (1).png b/docs/.gitbook/assets/image (1).png index 2efcff5829..de2be55f30 100644 Binary files a/docs/.gitbook/assets/image (1).png and b/docs/.gitbook/assets/image (1).png differ diff --git a/docs/.gitbook/assets/image (2) (1) (1) (1).png b/docs/.gitbook/assets/image (2) (1) (1) (1).png new file mode 100644 index 0000000000..bff8d6d115 Binary files /dev/null and b/docs/.gitbook/assets/image (2) (1) (1) (1).png differ diff --git a/docs/.gitbook/assets/image (2) (1) (1).png b/docs/.gitbook/assets/image (2) (1) (1).png index bff8d6d115..4793a1f109 100644 Binary files a/docs/.gitbook/assets/image (2) (1) (1).png and b/docs/.gitbook/assets/image (2) (1) (1).png differ diff --git a/docs/.gitbook/assets/image (2) (1) (3).png b/docs/.gitbook/assets/image (2) (1) (3).png new file mode 100644 index 0000000000..a541bdc545 Binary files /dev/null and b/docs/.gitbook/assets/image (2) (1) (3).png differ diff --git a/docs/.gitbook/assets/image (2) (1).png b/docs/.gitbook/assets/image (2) (1).png index a541bdc545..75381204f2 100644 Binary files a/docs/.gitbook/assets/image (2) (1).png and b/docs/.gitbook/assets/image (2) (1).png differ diff --git a/docs/.gitbook/assets/image (2).png b/docs/.gitbook/assets/image (2).png index 4793a1f109..9fc0c8426e 100644 Binary files a/docs/.gitbook/assets/image (2).png and b/docs/.gitbook/assets/image (2).png differ diff --git a/docs/.gitbook/assets/image (40).png b/docs/.gitbook/assets/image (40).png new file mode 100644 index 0000000000..8bb7a9605d Binary files /dev/null and b/docs/.gitbook/assets/image (40).png differ diff --git a/docs/.gitbook/assets/image (41).png b/docs/.gitbook/assets/image (41).png new file mode 100644 index 0000000000..936e1da226 Binary files /dev/null and b/docs/.gitbook/assets/image (41).png differ diff --git a/docs/.gitbook/assets/image (6) (4).png b/docs/.gitbook/assets/image (6) (4).png new file mode 100644 index 0000000000..31bf5e77dc Binary files /dev/null and b/docs/.gitbook/assets/image (6) (4).png differ diff --git a/docs/.gitbook/assets/image (6).png b/docs/.gitbook/assets/image (6).png index 31bf5e77dc..db3a31549d 100644 Binary files a/docs/.gitbook/assets/image (6).png and b/docs/.gitbook/assets/image (6).png differ diff --git a/docs/.gitbook/assets/image (7) (4).png b/docs/.gitbook/assets/image (7) (4).png new file mode 100644 index 0000000000..df872c85b9 Binary files /dev/null and b/docs/.gitbook/assets/image (7) (4).png differ diff --git a/docs/.gitbook/assets/image (7).png b/docs/.gitbook/assets/image (7).png index df872c85b9..1d74478286 100644 Binary files a/docs/.gitbook/assets/image (7).png and b/docs/.gitbook/assets/image (7).png differ diff --git a/docs/.gitbook/assets/image (8) (2) (1).png b/docs/.gitbook/assets/image (8) (2) (1).png new file mode 100644 index 0000000000..10ff2ca3f4 Binary files /dev/null and b/docs/.gitbook/assets/image (8) (2) (1).png differ diff --git a/docs/.gitbook/assets/image (8) (2).png b/docs/.gitbook/assets/image (8) (2).png index 10ff2ca3f4..7e227adaab 100644 Binary files a/docs/.gitbook/assets/image (8) (2).png and b/docs/.gitbook/assets/image (8) (2).png differ diff --git a/docs/.gitbook/assets/image (8).png b/docs/.gitbook/assets/image (8).png index 7e227adaab..3e5b4ed728 100644 Binary files a/docs/.gitbook/assets/image (8).png and b/docs/.gitbook/assets/image (8).png differ diff --git a/docs/.gitbook/assets/image (9) (3) (1).png b/docs/.gitbook/assets/image (9) (3) (1).png new file mode 100644 index 0000000000..53452c5f5c Binary files /dev/null and b/docs/.gitbook/assets/image (9) (3) (1).png differ diff --git a/docs/.gitbook/assets/image (9) (3).png b/docs/.gitbook/assets/image (9) (3).png index 53452c5f5c..f605b1086a 100644 Binary files a/docs/.gitbook/assets/image (9) (3).png and b/docs/.gitbook/assets/image (9) (3).png differ diff --git a/docs/.gitbook/assets/image (9).png b/docs/.gitbook/assets/image (9).png index f605b1086a..e195635e5a 100644 Binary files a/docs/.gitbook/assets/image (9).png and b/docs/.gitbook/assets/image (9).png differ diff --git a/docs/.gitbook/assets/image.png b/docs/.gitbook/assets/image.png index db3a31549d..936e1da226 100644 Binary files a/docs/.gitbook/assets/image.png and b/docs/.gitbook/assets/image.png differ diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index a7a91f39a1..352aaf7855 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -10,6 +10,7 @@ * [πŸ›  Installation](getting-started/getting-started/installation.md) * [πŸ”¨ How to create Operator ID and Operator Key](getting-started/getting-started/how-to-create-operator-id-and-operator-key.md) * [πŸ”¨ How to generate Web3.Storage API Key](getting-started/getting-started/how-to-generate-web3.storage-api-key.md) + * [πŸ”¨ How to Change Explorer URL](guardian/readme/getting-started/how-to-change-explorer-url.md) * [πŸ™ Contributing](getting-started/contributing/README.md) * [πŸš‡ Contribute a New Policy](getting-started/contributing/contribute-a-new-policy.md) * [πŸ“– Frameworks/Libraries](getting-started/built-with.md) @@ -99,6 +100,7 @@ * [splitBlock](available-policy-workflow-blocks/splitblock.md) * [wipeDocumentBlock](available-policy-workflow-blocks/token-wipe-workflow-block.md) * [Create Token Block](available-policy-workflow-blocks/create-token-block.md) + * [impactAddon](guardian/standard-registry/policies/introduction/impactaddon.md) * [πŸ’» Creating Policy using UI](policy-flow/policy-demo.md) * [πŸ’» Creating a Policy through Policy Configurator](guardian/standard-registry/policies/creating-a-policy-through-policy-configurator/README.md) * [Getting Started with the Policy Workflows](policy-workflow-creation-using-the-guardian-user-interface/getting-started-with-the-policy-workflows.md) @@ -172,6 +174,10 @@ * [πŸ’» Configuring Multi Policy using UI](guardian/standard-registry/policies/page-1.md) * [πŸ“’ Artifacts](guardian/standard-registry/artifacts/README.md) * [πŸ’» Importing/Deleting Artifacts using UI](artifacts-demo/how-to-import-delete-artifacts.md) + * [βš™ Artifacts APIs](guardian/standard-registry/artifacts/artifacts-apis/README.md) + * [Returns all Artifacts](guardian/standard-registry/artifacts/artifacts-apis/returns-all-artifacts.md) + * [Upload Artifacts](guardian/standard-registry/artifacts/artifacts-apis/upload-artifacts.md) + * [Delete Artifact](guardian/standard-registry/artifacts/artifacts-apis/delete-artifact.md) * [βš™ TrustChain APIs](guardian/standard-registry/trustchain-apis/README.md) * [Requesting](trustchains-apis/requesting.md) * [Building and returning](trustchains-apis/building-and-returning.md) @@ -227,10 +233,19 @@ * [Unsetting KYC for the User](token-related-apis-for-asynchronous-execution/unsetting-kyc-for-the-user.md) * [πŸ‘Ύ Automation Testing](guardian/automation-testing/README.md) * [πŸ’» Performing API Automation Testing](api-automation-testing/how-to-perform-api-automation-testing.md) + * [πŸ’» Performing UI Automation Testing](guardian/automation-testing/performing-ui-automation-testing.md) * [πŸ“ Demo Guide](guardian/demo-guide/README.md) - * [πŸ’» iREC 5 Demo UI Guide](demo-guide/irec-demo-guide.md) - * [πŸ’» iREC 7 Demo UI Guide](demo-guide/irec-7-demo-guide.md) - * [βš™ iREC API Demo Guide](demo-guide/api-workflow-of-irec-demo.md) - * [βš™ Demo Using APIs and UI](demo-guide/demo-using-apis.md) - * [πŸ’» Verra Redd+ Demo UI Guide](demo-guide/verra-redd+-demo-guide.md) + * [πŸ”‹ Renewable Energy Credits](guardian/demo-guide/renewable-energy-credits/README.md) + * [πŸ“– Introduction to International Renewable Energy Credit Standard (iREC)](guardian/demo-guide/renewable-energy-credits/introduction-to-international-renewable-energy-credit-standard-irec.md) + * [βš™ iREC API Demo Guide](guardian/demo-guide/renewable-energy-credits/api-workflow-of-irec-demo.md) + * [βš™ Demo Using APIs and UI](guardian/demo-guide/renewable-energy-credits/demo-using-apis.md) + * [πŸ’» iREC 5 Demo UI Guide](guardian/demo-guide/renewable-energy-credits/irec-demo-guide.md) + * [πŸ’» iREC 7 Demo UI Guide](guardian/demo-guide/renewable-energy-credits/irec-7-demo-guide.md) + * [☘ Carbon Offsets](guardian/demo-guide/carbon-offsets/README.md) + * [πŸ“– Introduction to Verra Redd+](guardian/demo-guide/carbon-offsets/introduction-to-verra-redd+.md) + * [πŸ’» Verra Redd+ Demo UI Guide](guardian/demo-guide/carbon-offsets/verra-redd+-demo-guide.md) + * [🏭 Carbon Emissions](guardian/demo-guide/carbon-emissions/README.md) + * [🏑 Remote Work GHG Policy](guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/README.md) + * [πŸ“– Introduction to Remote Work GHG](guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/introduction-to-remote-work-ghg.md) + * [πŸ’» Remote GHG Policy Demo Guide](guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/remote-ghg-policy-demo-guide.md) * [❓ FAQs](faqs/faqs.md) diff --git a/docs/available-policy-workflow-blocks/events.md b/docs/available-policy-workflow-blocks/events.md index dbdd35d997..8a310379fe 100644 --- a/docs/available-policy-workflow-blocks/events.md +++ b/docs/available-policy-workflow-blocks/events.md @@ -94,6 +94,17 @@ Input Events are received and processed by the egress interface of the β€˜Target * RestoreEvent - Which receive input vc document to restore data. +#### mintDocumentBlock: + +* AdditionalMintEvent - allows linking of the result of one mint block with another mint block. + +{% hint style="info" %} +**Note:** + +1. The link starts with β€œRunEvent” of the main Mint Block and ends with β€œAdditionalMintEvent” of the secondary Mint Block +2. β€œDefault Event” of the main Mint Block must be switched off if linked Mint Blocks follow each other +{% endhint %} + ### Event Actor Allows to change the user-context of the execution diff --git a/docs/available-policy-workflow-blocks/multisignblock.md b/docs/available-policy-workflow-blocks/multisignblock.md index f7c42269fb..384a8fa116 100644 --- a/docs/available-policy-workflow-blocks/multisignblock.md +++ b/docs/available-policy-workflow-blocks/multisignblock.md @@ -110,4 +110,4 @@ To get detailed information on Signature status, we have an info icon near the t To get the final Signature Result with detailed information such as which users have Signed / Declined, we need to hover on the Status as shown below: -
+
diff --git a/docs/available-policy-workflow-blocks/reportblock-and-reportitemblock.md b/docs/available-policy-workflow-blocks/reportblock-and-reportitemblock.md index 6ade1a310d..1e9800bf55 100644 --- a/docs/available-policy-workflow-blocks/reportblock-and-reportitemblock.md +++ b/docs/available-policy-workflow-blocks/reportblock-and-reportitemblock.md @@ -2,7 +2,7 @@ ## reportBlock -### Properties +### 1. Properties | Block Property | Definition | Example Input | Status | | -------------- | --------------------------------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------ | @@ -12,6 +12,31 @@ | defaultActive | Shows whether this block is active at this time and whether it needs to be shown. | Checked or unchecked. | | | dependencies | Establish workflow dependancies that need to be completed prior. | Select the appropriate block from the dropdown. | Deprecated | +### 2. Impacts Section + +We have added new Impacts Section to display Primary/Secondary Impacts token details in Trustchain: + +
+ +In the case when multiple linked mint blocks are used then the system displays all linked VPs as shown below: + +
+ +#### 2.1 Data Format: + +Ingress Document has following sections: + +| Document Type | Description | +| --------------------- | ------------------------------------------------------------- | +| vpDocument | VP document found based on its hash | +| vcDocument | VC document found based on its hash | +| impacts (new) | array of Impacts (VCs) if exist | +| mintDocument | VC document describing the mint | +| policyDocument | VC document describing the policy | +| policyCreatorDocument | VC document describing the Standard Registry | +| documents | collection of VC documents retrieved from the reportItemBlock | +| additionalDocuments | array of VPs linked with the selected document | + ### Events | Property Name | Name in JSON | Property Value | Value in JSON | Description | diff --git a/docs/available-policy-workflow-blocks/tokenconfirmationblock.md b/docs/available-policy-workflow-blocks/tokenconfirmationblock.md index 6263f3abdb..5371c2bafd 100644 --- a/docs/available-policy-workflow-blocks/tokenconfirmationblock.md +++ b/docs/available-policy-workflow-blocks/tokenconfirmationblock.md @@ -28,13 +28,13 @@ This block enables the owner of the private key for the account to manually perf 3. Users can skip the action by selecting β€œI will _Action_ manually” option in the UI. This would require the user to perform the corresponding action off-Guardian platform (directly on Hedera chain). {% endhint %} -![Block Properties](<../.gitbook/assets/image (6).png>) +![Block Properties](<../.gitbook/assets/image (6) (4).png>) -![JSON View of the Block](<../.gitbook/assets/image (9).png>) +![JSON View of the Block](<../.gitbook/assets/image (9) (3).png>) ![Configuring tokenConfirmationBlock](<../.gitbook/assets/image (1) (3) (1).png>) -![Creating Event to move to next step](<../.gitbook/assets/image (8).png>) +![Creating Event to move to next step](<../.gitbook/assets/image (8) (2).png>) ### 2. Performing Token Associate using Guardian UI diff --git a/docs/faqs/faqs.md b/docs/faqs/faqs.md index a1219bc15e..c1e76fbf71 100644 --- a/docs/faqs/faqs.md +++ b/docs/faqs/faqs.md @@ -70,8 +70,8 @@ In the guardian a VP is a VP. It's just an object with schemas and dids inside o Here are the steps you should follow to get a VP from a token mint transaction. -Look for a token mint transaction on [DragonGlass](https://app.dragonglass.me/).\ -For the transaction `00341948921649942497135549961` the link will be [https://testnet.dragonglass.me/transactions/00341948921649942497135549961](https://testnet.dragonglass.me/transactions/00341948921649942497135549961)\ +Look for a token mint transaction on [Ledger Works](https://explore.lworks.io/).\ +For the transaction `00341948921649942497135549961` the link will be [https://explore.lworks.io/testnet/transactions/0.0.34194892-1649942497-135549961](https://explore.lworks.io/testnet/transactions/0.0.34194892-1649942497-135549961)\ Transaction data will contain data similar to this: ``` @@ -246,12 +246,12 @@ DID is the the DID of the RA, i.e. is a globally unique reference linking to a D When sending messages to the header, the Message Id and Topic Id are written to the database You can view them using the messageId and topicId fields -All messages in the topic can be viewed at the link https://testnet.dragonglass.me/hedera/topics/{topicId} +All messages in the topic can be viewed at the link [https://explore.lworks.io/testnet/topics/0.0.47802089](https://explore.lworks.io/testnet/topics/0.0.47802089) for example**:** -https://testnet.dragonglass.me/hedera/topics/0.0.47802089 +[https://explore.lworks.io/testnet/topics/{topicId}](https://explore.lworks.io/testnet/topics/%7BtopicId%7D) -Unfortunately, dragonglass does not have a search by messageId, but messages can be obtained using mirrornode +Unfortunately, Ledger Works does not have a search by messageId, but messages can be obtained using mirrornode for example https://testnet.mirrornode.hedera.com/api/v1/topics/messages/1659539040.128851003 diff --git a/docs/guardian/automation-testing/performing-ui-automation-testing.md b/docs/guardian/automation-testing/performing-ui-automation-testing.md new file mode 100644 index 0000000000..17ea1c9e98 --- /dev/null +++ b/docs/guardian/automation-testing/performing-ui-automation-testing.md @@ -0,0 +1,41 @@ +# πŸ’» Performing UI Automation Testing + +Following command is used to run all UI tests in an interactive dashboard that allows you to see the status of the tests while they are running and at the same time view the application under test. + +``` +npm run ui-only +``` + +To run a UI test for specific policy you can open Cypress dashboard by running the following command from /e2e-tests folder: + +``` +npx cypress open +``` + +After executing this command, you will see a welcome window where you need to select + +'E2E Testing' as shown below: + +
+ +Click on the button ' Start E2E Testing in Electron '. + +
+ +and then select test under ui-tests/specs/policies + +
+ +Finally, all the selected test runs and you can see the key components of the Test Runner that you need to pay attention to when executing tests. + +
+ +**Test Status Menu:** The menu shows a summary of the number of tests passed, passed, failed, or incomplete, and the time spent on the test. + +**URL Preview:** Shows the URL of the test, helps track the URL. + +**Viewport Size:** Set the size of your application's viewport to test responsive web applications. + +**Command Log:** Shows command logs as they run for all tests. In Cypress runner we can observe the requests that are sent to the server. + +**App Preview Screen:** You can see the app while the test is running. diff --git a/docs/guardian/demo-guide/carbon-emissions/README.md b/docs/guardian/demo-guide/carbon-emissions/README.md new file mode 100644 index 0000000000..99625902bb --- /dev/null +++ b/docs/guardian/demo-guide/carbon-emissions/README.md @@ -0,0 +1,2 @@ +# 🏭 Carbon Emissions + diff --git a/docs/guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/README.md b/docs/guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/README.md new file mode 100644 index 0000000000..ee219b64b8 --- /dev/null +++ b/docs/guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/README.md @@ -0,0 +1,2 @@ +# 🏑 Remote Work GHG Policy + diff --git a/docs/guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/introduction-to-remote-work-ghg.md b/docs/guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/introduction-to-remote-work-ghg.md new file mode 100644 index 0000000000..6f8d9e61a2 --- /dev/null +++ b/docs/guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/introduction-to-remote-work-ghg.md @@ -0,0 +1,15 @@ +# πŸ“– Introduction to Remote Work GHG + +## Policy Description + +This policy supports the estimation, calculation, and tokenization of GHG emissions resulting from remote work. This policy can be used as a stand-alone methodology, or a methodology to estimate organizational Scope 3 GHG emissions from remote work to be incorporated into organizational GHG inventories in alignment with the GHG Protocol Corporate Standard and/or the GHG Protocol Scope 3 Standard. To support integration with the GHG Protocol Policies, both location and market-based emissions are calculated. + +This methodology estimates electric and natural gas consumption per hour for each employee, which is refined based on the results of a detailed survey. To estimate and track GHG emissions in near-real-time, employees track, and report hours worked and GHG emissions will be estimated as hours are logged. For employees who do not respond to the survey, either default values will be used or data from respondents can be apportioned among non-respondents. + +Heating and cooling energy consumption defaults were calculated based on Energy Information Administration Residential Energy Consumption Survey microdata, which was customized to specific equipment types and climate regions. + +## **Workflow Description**: + +The workflow begins with each employee completing a survey to help refine their GHG estimates based on location; actual office, heating, and cooling equipment; and other important variables. Based on the responses, a verified credentials (VC) document is created. Then, employees track hours worked and GHG emissions are auto-calculated by customized schemas featuring built-in equations, defaults, and emission factors. Workforce emissions are calculated as the sum of all employee emissions and the resulting emissions are tokenized. + +
diff --git a/docs/guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/remote-ghg-policy-demo-guide.md b/docs/guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/remote-ghg-policy-demo-guide.md new file mode 100644 index 0000000000..437731f754 --- /dev/null +++ b/docs/guardian/demo-guide/carbon-emissions/remote-work-ghg-policy/remote-ghg-policy-demo-guide.md @@ -0,0 +1,102 @@ +# πŸ’» Remote GHG Policy Demo Guide + +### For Organizations to Create an Employer (Employer Admin User) + +Typically, the way we start the demonstration is by logging in as a Standard Registry called "GHG Remote Work". Create a user named "GHG Remote Work." + +You'll now be prompted to configure your GHG Remote Work account. Enter the details and then press the Generate button to generate a Hedera Operator ID and an Operator Key and enter the name of your Standard Registry. Press Connect when finished. This will now create Hedera Consensus Service Topics, fill the account with test hBar, create a DID document, create a Verifiable Credential, etc + +
+ +Now we will be creating the Policy. We have three ways to "create policies." The first way is to actually create the policy from scratch. The second way is to import an existing policy; either the policy file itself or from IPFS. When you import a policy, all schemas and tokens that are required in the policy are automatically populated. To do this, you can find the policy file and the IPFS timestamp on the open-source Guardian policy page [here](https://github.com/hashgraph/guardian/tree/main/Demo%20Artifacts/iREC). For this demo guide, we will be using the 3rd way to create a policy, which is through the preloaded drop-down list. + +Once it is selected, we can also preview the policy before importing it. After the policy is imported, we can either run the policy in Dry run mode or we can publish it by clicking on publish button from the dropdown. For testing purposes, we will publish the policy. + +Once the policy is published, we will log out and create a new user called, "Employer". + +In the Profile screen, select β€œGHG Remote Work” and then click the β€œGenerate” button. + +
+ +When the Operator ID and Operator Key generates, click the submit button. + +
+ +When the Employer profile has been created, click on the Policies tab + +
+ +In the Policies tab, click on the β€œGo” button to begin the Remote Work GHG Policy operations. + +
+ +To create a new Organization Group, select the β€œorganization group template” and provide a Group Label (i.e. organization\_group). Click Ok when you are finished. + +
+ +Fill out the Employer Info. Note that a valid Hedera Treasury Account is required. Enter your Private Key to associate your Organization Account with the GHG token. + +
+ +In the Employee sub-tab, click on the β€œGet Invite” button to generate the link that Employees will use to join the Organization Group. + +
+ +Generate the invite for the Employee role. + +
+ +Copy the invitation and then send the invitation to employees you wish to join the Organization Group. + +
+ +As Employees join your Organization Group and fill out the Employee Survey, their results will show up in the Policy Screen under the sub-tab Employee Survey column. You can view their results by clicking on β€œView Document.” + +
+ +You can review their Time Tracking submissions in the Policy Screen under the sub-tab Report column. View their results by clicking on β€œView Document.” + +
+ +You can review the total aggregate CO2 emissions in the Policy Screen under the sub-table Total Report column. You can view their results by clicking on β€œView Document.” + +{% hint style="info" %} +Note: + +1. The timers in the GHG policy on MGS are set to intervals of hours, so you need to wait \~1.5 hours for minting tokens +2. You can make changes to this time period by changing time\_tracking\_timer block from period to custom. + +**For example** : When we change period from custom to 1m and mask as 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,57,59 \* \* \* \* \* +{% endhint %} + +You can view the number of tokens minted in the Policy Screen under the sub-table Mint’s VP column. You can explore the Trust Chain by clicking on the β€œView Document.” + +
+ +### For Employees to Create an Employee User and Join an Organization (Employee Users) + +Log in and create a user with the name of your choice. For the demo, we are using the name Employee. + +On the Profile screen, select β€œGHG Remote Work” and then β€œGenerate” the Operator ID and Operator Key information. When the Operator ID and Operator Key generates, click the submit button. + +When the Employee profile has been created, click on the Policies tab. + +
+ +In the Policies tab, click on the β€œGo” button to begin the Remote Work GHG Policy operations. + +
+ +To join the Organization, select β€œAccept invitation” and copy and paste the invite link that the Employee sent. + +
+ +Fill out the Employee Survey. + +In the Time Tracking sub-tab, click on the Create New Time Tracking button. + +
+ +Fill out the form to track working hours. Repeat this step for as many days as you are looking to track. In the screenshot below, we are showing an example of tracking time for two days (Sept. 28 and Sept. 29). + +
diff --git a/docs/guardian/demo-guide/carbon-offsets/README.md b/docs/guardian/demo-guide/carbon-offsets/README.md new file mode 100644 index 0000000000..6f953d3071 --- /dev/null +++ b/docs/guardian/demo-guide/carbon-offsets/README.md @@ -0,0 +1,2 @@ +# ☘ Carbon Offsets + diff --git a/docs/guardian/demo-guide/carbon-offsets/introduction-to-verra-redd+.md b/docs/guardian/demo-guide/carbon-offsets/introduction-to-verra-redd+.md new file mode 100644 index 0000000000..124ee8fb96 --- /dev/null +++ b/docs/guardian/demo-guide/carbon-offsets/introduction-to-verra-redd+.md @@ -0,0 +1,17 @@ +# πŸ“– Introduction to Verra Redd+ + +## Policy Description: + +This policy supports the tokenization of carbon offsets, specifically Verified Carbon Units (VCUs) in accordance with Verra’s Verified Carbon Standard (VCS), and specifically, the VM0007 REDD+ Methodology Framework (REDD+ MF). Verra develops and manages standards that are globally applicable and advance action across a wide range of sectors and activities. The VCS Program is the world’s most widely used voluntary GHG program. + +The workflow is designed to reflect the general roles and processes of the VCS program. The VM0007 REDD+ is a dynamic modular methodology, and the specific requirements vary depending on the specific project activities and context. The schema was designed to capture the monitoring parameters required for the Avoiding Planned Deforestation (ADP) project type. Projects that involve other project types, and specific contexts, such as projects that involve harvesting commercial timber, may have additional parameters and MRV requirements. + +## **Workflow Description**: + +The workflow begins with the Project Proponent submitting the project description (PD) to Verra. The project description will include ex-ante estimates of the monitoring parameters, as well as other project details to demonstrate alignment with the VCS program requirements. Verra then adds the PD and project docs to the Project Pipeline and Project Registry. Next, the Validation and Verification Body (VVB) will assess and validate the PD and provide a Validation Report. Then the Project Proponent will conduct monitoring and develop a Monitoring Report, replacing the ex-ante estimates with ex-post MRV data and submitting it to the VVB. The VVB then verifies the Monitoring Report and delivers a Verification Report, Verification Representation, and Verification Statement. Finally, Verra reviews the issuance request, and VCUs are issued to the Project Proponent. + +In future iterations, the workflow will begin with the Project Proponent completing a questionnaire to determine the appropriate methodology, modules, and MRV requirements. Then branch functionality can enable the automatic creation of context-specific schemas. + +Roles in the below Workflow Diagram are represented as follows: Project Proponent (Blue), Verra (Orange), VVB (Green) + +
diff --git a/docs/demo-guide/verra-redd+-demo-guide.md b/docs/guardian/demo-guide/carbon-offsets/verra-redd+-demo-guide.md similarity index 74% rename from docs/demo-guide/verra-redd+-demo-guide.md rename to docs/guardian/demo-guide/carbon-offsets/verra-redd+-demo-guide.md index 98d40748c0..a245f65bcc 100644 --- a/docs/demo-guide/verra-redd+-demo-guide.md +++ b/docs/guardian/demo-guide/carbon-offsets/verra-redd+-demo-guide.md @@ -13,11 +13,11 @@ This folder contains a sample file that is referenced in the Demo Usage Guide 2\. After running the installation commands, open a tab on your browser and navigate to [http://localhost:3000/](http://localhost:3000/). Typically, the way we start the reference implementation demonstration is by logging in as Verra. Click the Demo Admin Panel drop-down located in the upper right-hand corner of the login screen and select the Verra user. -![](../.gitbook/assets/Verra\_1.png) +![](../../../.gitbook/assets/Verra\_1.png) 3\. You'll now be prompted to configure your Verra account. Enter the details and then press the Generate button to generate a Hedera Operator ID and an Operator Key and enter the name of your Standard Registry. Press Connect when finished. This will now create Hedera Consensus Service Topics, fill the account with test hBar, create a DID document, create a Verifiable Credential, etc. -![](../.gitbook/assets/Verra\_2.2.png) +![](../../../.gitbook/assets/Verra\_2.2.png) 4\. This could be one of the most interesting parts of the reference implementation. Now we will be creating the Policy. We have two ways to "create policies." The first way is to import an existing policy. This is the easiest way to get started. When you import a policy, all schemas and tokens that are required in the policy are automatically populated. To do this, you can use the sample policy that we have already uploaded to IPFS. Click on the import button and enter the one of the following Hedera message IDs: @@ -27,71 +27,71 @@ REDD 2.2.2 = 1661882417.772439852 Verra REDD Policy 3 groups (1665667787.019450003).policy ``` -
+
Once Policy is imported successfully, you get two options : Publish and Dry Run mode. -
+
We select publish option to publish the Policy to the Hedera network. -
+
5\. Click on Standard Registry profile icon and select "Log Out." We will now go back into the Admin Panel. This time we will select VVB. We select Standard Registry in the dropdown, generate Operator ID and Operator Key and submit. -
+
6\. Now, we can click on the Policies tab. This is where the specific actions required by the Policy Workflow will be found. We can click Go button to the right of the Verra Policy, the Verra created. -
+
7\. We have an option to create a group or accept an invite. In this case, we create a new group by selecting VVBs group from the dropdown and adding a Group label as shown below: -
+
-
+
8\. Once the group is created, we need to create VVB by entering VVB name. -
+
Once, VVB name is submitted, its waiting for the approval. 9\. Now, we logout as VVB and login as Standard Registry and review the document and approve VVB by clicking on Approve button. -
+
10\. Now we login as Project Proponent and repeat same steps as Step 5, Step 6. 11\. Now, we select Project Proponent from the drop down in the Group and also enter the Group label as shown below. -
+
After creating the group, we will see the New Project button. When we click on the button, we get a form, where complete project details need to be added. We have uploaded a [Sample test data](https://github.com/hashgraph/guardian/blob/main/Demo%20Artifacts/REDD%20APD%20Schema%20Design%20Template.xlsx) file in our repo. -![](../.gitbook/assets/verra\_new\_8.png) +![](../../../.gitbook/assets/verra\_new\_8.png) 12\. The next step of our flow is to log out and sign back in as the Standard Registry. Navigate to the Policies tab and click the Open button on the far right. Here you will find the approval actions based on our Policy Workflow required by Verra. You will be able to view the Verifiable Credential prior to approval by selecting the View Document link. Once you are ready to approve the project details, you can click on the Add button. -
+
13\. Now, we login as Project Proponent and select VVB from the dropdown of Assign column for Validating the project details. -
+
14\. The next step of our flow is to log out and sign back in as the VVB. We click on Members tab in the Policy and click on Get Invite button to create an invitation link. This invitation link is created to create VVB member. -
+
-
+
15\. Now, we create a new user as VVBNew and paste the link under accept invitation section as shown below: -
+
16\. Once clicked on OK in above screen, we now have an option to sign/ decline the Project. For demo purpose, we click on Sign button to validate the Project. -
+
{% hint style="info" %} Note: If there are more than one VVB, we need atleast 70% consensus to get the finalized decision on the Project to proceed. @@ -99,20 +99,20 @@ Note: If there are more than one VVB, we need atleast 70% consensus to get the f 16\. Once the Project is validated, we log out as VVB and login as Project Proponent. Monitoring Report details should be added by clicking on Add Report -
+
17\. Now, you log out and login to VVBNew. You will be able to view the Verifiable Credential by selecting View Document button. Once, you are ready to verify the monitoring report details, you can click on Sign button. -
+
18\. Once, Monitoring report is verified, you log out as VVBNew and login as Standard Registry. Navigate to the Policies tab and click the Open button on the far right. You will be able to see Monitoring Reports tab, where you have an option to click on Mint button. -
+
18\. Once, Minting process is completed, navigate to the Token History tab, where you have an option of viewing TrustChain. You can view TrustChain by clicking on View TrustChain button. -![](../.gitbook/assets/verra\_new\_16.png) +![](../../../.gitbook/assets/verra\_new\_16.png) The Trust Chain view displays essential elements that can be publicly discovered. Elements include token information, Policy information, and all the essential information regarding the Verifiable Credentials that make up the Verifiable Presentation. You will notice "Cards" on the bottom of the screen. Those cards are Verifiable Credentials displayed in chronological order. -![](../.gitbook/assets/verra\_new\_17.png) +![](../../../.gitbook/assets/verra\_new\_17.png) diff --git a/docs/guardian/demo-guide/renewable-energy-credits/README.md b/docs/guardian/demo-guide/renewable-energy-credits/README.md new file mode 100644 index 0000000000..609cdeba17 --- /dev/null +++ b/docs/guardian/demo-guide/renewable-energy-credits/README.md @@ -0,0 +1,2 @@ +# πŸ”‹ Renewable Energy Credits + diff --git a/docs/demo-guide/api-workflow-of-irec-demo.md b/docs/guardian/demo-guide/renewable-energy-credits/api-workflow-of-irec-demo.md similarity index 100% rename from docs/demo-guide/api-workflow-of-irec-demo.md rename to docs/guardian/demo-guide/renewable-energy-credits/api-workflow-of-irec-demo.md diff --git a/docs/demo-guide/demo-using-apis.md b/docs/guardian/demo-guide/renewable-energy-credits/demo-using-apis.md similarity index 97% rename from docs/demo-guide/demo-using-apis.md rename to docs/guardian/demo-guide/renewable-energy-credits/demo-using-apis.md index f8ccd0c5f5..fbc8180aa7 100644 --- a/docs/demo-guide/demo-using-apis.md +++ b/docs/guardian/demo-guide/renewable-energy-credits/demo-using-apis.md @@ -28,7 +28,7 @@ ### 1.2 In the policy config there is a root block which is the top of the structure -![](<../.gitbook/assets/API\_1 (1).png>) +![](<../../../.gitbook/assets/API\_1 (1).png>) ### 1.3 Request the config for the root block @@ -70,7 +70,7 @@ {% endswagger-response %} {% endswagger %} -![](<../.gitbook/assets/API\_2 (1) (1).png>) +![](<../../../.gitbook/assets/API\_2 (1) (1).png>) ### 1.5 At present only PolicyRolesBlock is available to the user. Select the "INSTALLER" role. @@ -84,7 +84,7 @@ INSTALLER {% endswagger-parameter %} {% endswagger %} -![](../.gitbook/assets/API\_3.png) +![](../../../.gitbook/assets/API\_3.png) ### 1.6 Request the root block and all contained blocks. @@ -235,7 +235,7 @@ Years of registration {% endswagger-parameter %} {% endswagger %} -![](<../.gitbook/assets/API\_4 (1) (1).png>) +![](<../../../.gitbook/assets/API\_4 (1) (1).png>) ### 1.8 Request the root block and all contained blocks again. @@ -297,7 +297,7 @@ Years of registration {% endswagger-response %} {% endswagger %} -![](<../.gitbook/assets/image 1.png>) +![](<../../../.gitbook/assets/image 1.png>) ## 2. Login as a Standard Registry @@ -323,7 +323,7 @@ Years of registration {% endswagger-response %} {% endswagger %} -![](../.gitbook/assets/API\_6.png) +![](../../../.gitbook/assets/API\_6.png) ### 2.2 Request the root block and all contained blocks. @@ -471,9 +471,9 @@ POST /api/v1/policies/621376c8e6763a0014fb0de4/blocks/6f0f37c0-b62b-4be5-b1d0-e ``` -![](../.gitbook/assets/API\_7.png) +![](../../../.gitbook/assets/API\_7.png) -![](<../.gitbook/assets/image 2.png>) +![](<../../../.gitbook/assets/image 2.png>) ## 3. Login as the User @@ -699,9 +699,9 @@ capacity {% endswagger-parameter %} {% endswagger %} -![](../.gitbook/assets/Sensor.png) +![](../../../.gitbook/assets/Sensor.png) -
+
### 3.4 Refresh the Blocks @@ -717,7 +717,7 @@ record in the grid (data[0]) {% endswagger-parameter %} {% endswagger %} -![](<../.gitbook/assets/image 4.png>) +![](<../../../.gitbook/assets/image 4.png>) ### 3.6 Sample MRV Sender Data diff --git a/docs/guardian/demo-guide/renewable-energy-credits/introduction-to-international-renewable-energy-credit-standard-irec.md b/docs/guardian/demo-guide/renewable-energy-credits/introduction-to-international-renewable-energy-credit-standard-irec.md new file mode 100644 index 0000000000..ec3836e8cc --- /dev/null +++ b/docs/guardian/demo-guide/renewable-energy-credits/introduction-to-international-renewable-energy-credit-standard-irec.md @@ -0,0 +1,11 @@ +# πŸ“– Introduction to International Renewable Energy Credit Standard (iREC) + +## **Policy Description**: + +This policy supports the tokenization of Renewable Energy Certificates (RECs) in accordance with the I-REC Standard, and specifically, the I-REC(E) Product Code. The I-REC Standard is a non-profit organization that provides an attribute tracking standard that can be used around the world. While the I-REC Standard is designed to track attributes for a diversity of products, Product Codes provide additional requirements for specific products and markets. The I-REC(E) Product Code provides requirements for electricity products and was developed by Evident, who acts as Code Manager and Registry Operator. The schema and workflow of this policy were designed to reflect the MRV requirements, processes, and roles outlined by both I-REC Standard and the I-REC(E) Product Code. + +## **Workflow Description**: + +The workflow begins with the Registrant, generally the owner of an energy production facility or a party acting on their behalf, submitting an application to the Issuer for approval. Once approved, the Registrant submits a registration request to the Issuer for the facility/device that will be providing the MRV data. This will include both general information, as well as attributes (e.g., energy sources, location, etc.). Note that devices must be, and often are already, independently verified. Under certain circumstances, an inspection may be necessary. Once the Issuer processes and approves the facility/device registration, an issue request can be sent to the Issuer along with independently verified meter data. After the Issuer approves the issue request, I-REC(E) certificates are issued + +
diff --git a/docs/demo-guide/irec-7-demo-guide.md b/docs/guardian/demo-guide/renewable-energy-credits/irec-7-demo-guide.md similarity index 56% rename from docs/demo-guide/irec-7-demo-guide.md rename to docs/guardian/demo-guide/renewable-energy-credits/irec-7-demo-guide.md index 114a5c22cc..09b9600d5a 100644 --- a/docs/demo-guide/irec-7-demo-guide.md +++ b/docs/guardian/demo-guide/renewable-energy-credits/irec-7-demo-guide.md @@ -8,60 +8,60 @@ Following are the steps to run iREC 7 Policy. 1. We need to import the policy by using two steps : Either by importing the `IRec Policy 7 split documents (1666798058.496271367).policy` or through IPFS by entering the timestamp of the policy : 1666798058.496271367. -
+
2\. Once it is selected, we can also preview the policy before importing it. To import, click on Import button as shown below: -
+
3\. Once imported, we can either run the policy in Dry run mode or we can publish it by clicking on publish button from the dropdown. For testing purpose, we will publish the policy. -

We will publish the policy now.

+

We will publish the policy now.

4\. Once the policy is published, we will logout and login as Registrant.Once we login as Registrant, we will setup Registrant role by selecting Standard Registry from the dropdown, generating Operator ID and Operator Key as shown below: -
+
5\. Then, we need select Registrant role from the drop down and fill the Registrant application and submit it for approval from Standard Registry: -
+
-
+
-
+
6\. Now, we login as Standard Registry and approve the Registrant role by clicking on Approve button: -
+
7\. Once approved, we logout as Standard Registry and login as Registrant. As a registrant, we register device by entering all the device details. -
+
-
+
Once device details are entered, they are submitted for approval. 8\. We log back in as SD and after checking the device details, we can either approve/reject the device. We approve the device for now. -
+
9\. Once device is approved, we log back in as Registrant and click on Create Issue Request as shown below: -
+
10\. Registrant fills the Issue Request form and submits it for approval. -
+
11\. We login as SD and view the Issue Request and approve it by clicking on approve button. Once Issue request is approved, tokens are minted if the MRV data is more than 1000 KW as it is the threshold for splitting the data into chunks. -
+
12\. We can check the Tokens minting status and Trustchain in Token History tab of Standard Registry: -
+
13\. To view the trustchain, we need to click on View Trustchain in Token Histroy tab or Trustchain tab. -
+
diff --git a/docs/demo-guide/irec-demo-guide.md b/docs/guardian/demo-guide/renewable-energy-credits/irec-demo-guide.md similarity index 71% rename from docs/demo-guide/irec-demo-guide.md rename to docs/guardian/demo-guide/renewable-energy-credits/irec-demo-guide.md index 4df57a67db..4b4d7e2aca 100644 --- a/docs/demo-guide/irec-demo-guide.md +++ b/docs/guardian/demo-guide/renewable-energy-credits/irec-demo-guide.md @@ -18,7 +18,7 @@ This folder contains a sample file that is referenced in the Demo Guide 3\. You will now be prompted to configure your Standard Registry account. Press the Generate button to generate a Hedera Operator ID and an Operator Key and enter the details of your Standard Registry. Press Connect when finished. This will now create Hedera Consensus Service Topics, fill the account with test hBar, create a DID document, create a Verifiable Credential, etc. -![](../.gitbook/assets/iREC\_4.2.png) +![](../../../.gitbook/assets/iREC\_4.2.png) 4\. This could be one of the most interesting parts of the reference implementation. Now we will be creating the Policy. We have two ways to "create policies." The first way is to import an existing policy. This is the easiest way to get started. When you import a policy, all schemas and tokens that are required in the policy are automatically populated. To do this, you can use the sample policy that we have already uploaded to IPFS by entering the Hedera Message IDs. @@ -30,15 +30,15 @@ iREC 4 : 1662642008.325450377 iREC 5 : 1663850151.496004277 ``` -
+
Once clicked on OK, we have an option to Preview the Policy before importing it. -
+
Once Policy is imported successfully, you have two options : 1. to publish the policy, 2. To run the policy in Dry Run mode. -
+
Let's click on Publish button to publish the Policy to Hedera network. @@ -46,41 +46,41 @@ Let's click on Publish button to publish the Policy to Hedera network. 6\. Now, we can click on the Policies tab. This is where the specific actions required by the Policy Workflow will be found. We can click the Open button to the right of the iREC Policy, the Standard Registry created. This is where the custom user will be able to assign the role that was created by Standard Registry during the workflow creation process. In our case, we created the custom role of Registrant so the user will need to select the Registrant role from the drop down. -
+
-
+
7\. Now, we create a group by selecting Registrant in the Dropdown. or we enter an invite link of the group received to join that particular group in the Accept invitation option. -
+
8\. Once Registrant option is selected from the dropdown, we get Group label field enabled. Group label can be given anything of our choice. Here, we are giving as Registrant Group for testing purpose. -
+
Once, Group label is entered, we click on OK button. 9\. Now we enter the Registrant Application details and click on Ok. -
+
Once Submitted, Registrant waits for the approval of the application by Approver. 10\. Next step is logout as Registrant and login as Approver. This can be done by selecting Installer from the Admin panel and then selecting Standard Registry from the dropdown in Profile tab. -
+
11\. Once Profile is created, we click on Policies tab and click on Go to view the Policy. -
+
12\. Same as Registrant, you have an option to create group or accept invitation. But, the only difference over here is you need to select Approvers option in the group dropdown. -
+
13\. Once Approver is created, now we get an option to Sign/Decline the Registrant application. -
+
{% hint style="info" %} Note: If there are multiple approvers, we would need minimum of 70% consensus to reach the final result for an application. @@ -92,13 +92,13 @@ Note: If there are multiple approvers, we would need minimum of 70% consensus to 16\. When one of the approver, signs or declines the application by clicking on Sign button, we get the progress bar of the result as shown below -
+
{% hint style="info" %} To get complete information on the status, we need to click on "i" icon {% endhint %} -
+
17\. For demo purpose, we will also sign the above application as an Approver by logging in as an Approver and following the step 15. @@ -106,17 +106,17 @@ To get complete information on the status, we need to click on "i" icon 19\. Now, we login back as Registrant and click on Create New Device. -
+
20\. Device Registration details needs to be filled and submitted. -
+
Once details are submitted, Registrant waits for the approval. 21\. Now, we login back as Approver1 and approves the device created by Registrant. Device is approved by clicking on Sign button. -
+
22\. We logout as an Approver1 and login as Approver2 and approve the device by following step 21. @@ -124,19 +124,19 @@ Once details are submitted, Registrant waits for the approval. 24\. Now, the Registrant creates an Issue Request by clicking on Create Issue Request. -
+
25\. Issue Request is created by entering the details as shown below. -
+
26\. Once the Issue Request is submitted, the request status can be checked by clicking on Issue Requests tab. -
+
27\. Now, we login as Approver1 and go to Issue Requests tab and click on Sign button to approve the Issue Request. We can also view the document by clicking on View Document button. -
+
28\. We will logout as Approver1 and login as Approver2 and follow same steps as Step 27. @@ -144,10 +144,10 @@ Once details are submitted, Registrant waits for the approval. 30\. Now we login as Registrant and can check the Token History by clicking on Token History tab. -
+
31\. To view TrustChain, we logout as Registrant and login as Standard Registry and click on View TrustChain. -
+
-
+
diff --git a/docs/guardian/readme/getting-started/how-to-change-explorer-url.md b/docs/guardian/readme/getting-started/how-to-change-explorer-url.md new file mode 100644 index 0000000000..d0cd624a77 --- /dev/null +++ b/docs/guardian/readme/getting-started/how-to-change-explorer-url.md @@ -0,0 +1,18 @@ +# πŸ”¨ How to Change Explorer URL + +To make changes in the Explorer, we need to change some parameters in environment settings on UI. explorerSettings, which contains url (with network, type, value variables) , networkMap, typeMap (networkMap and typeMap helps to resolve api path on different explorers) as shown: + +
+ +As we see the above demonstrates setting of Ledger Works explorer. + +{% hint style="info" %} +**Note: By default, we use Ledger Works explorer.** +{% endhint %} + +#### To switch from Ledger Works to Dragon Glass we need to make following changes: + +1. Change the url to [https://${network}.dragonglass.me/hedera/${type}/${value}](https://${network}.dragonglass.me/hedera/$%7Btype%7D/$%7Bvalue%7D) +2. Change networkMap mainnet to app + +
diff --git a/docs/guardian/standard-registry/artifacts/artifacts-apis/README.md b/docs/guardian/standard-registry/artifacts/artifacts-apis/README.md new file mode 100644 index 0000000000..7732bebb2d --- /dev/null +++ b/docs/guardian/standard-registry/artifacts/artifacts-apis/README.md @@ -0,0 +1,2 @@ +# βš™ Artifacts APIs + diff --git a/docs/guardian/standard-registry/artifacts/artifacts-apis/delete-artifact.md b/docs/guardian/standard-registry/artifacts/artifacts-apis/delete-artifact.md new file mode 100644 index 0000000000..ec991daa90 --- /dev/null +++ b/docs/guardian/standard-registry/artifacts/artifacts-apis/delete-artifact.md @@ -0,0 +1,49 @@ +# Delete Artifact + +{% swagger method="delete" path="" baseUrl="/artifact/{artifactId}" summary="Delete artifact." %} +{% swagger-description %} +Delete artifact. +{% endswagger-description %} + +{% swagger-parameter in="path" name="artifactID" type="String" required="true" %} +Artifact identifier +{% endswagger-parameter %} + +{% swagger-response status="200: OK" description="Successful Operation" %} +```javascript +{ + content: + application/json: + schema: + type: boolean +} +``` +{% endswagger-response %} + +{% swagger-response status="401: Unauthorized" description="Unauthorized" %} +```javascript +{ + // Response +} +``` +{% endswagger-response %} + +{% swagger-response status="403: Forbidden" description="Forbidden" %} +```javascript +{ + // Response +} +``` +{% endswagger-response %} + +{% swagger-response status="500: Internal Server Error" description="Internal Server Error" %} +```javascript +{ + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +} +``` +{% endswagger-response %} +{% endswagger %} diff --git a/docs/guardian/standard-registry/artifacts/artifacts-apis/returns-all-artifacts.md b/docs/guardian/standard-registry/artifacts/artifacts-apis/returns-all-artifacts.md new file mode 100644 index 0000000000..8894ed2d03 --- /dev/null +++ b/docs/guardian/standard-registry/artifacts/artifacts-apis/returns-all-artifacts.md @@ -0,0 +1,64 @@ +# Returns all Artifacts + +{% swagger method="get" path="" baseUrl="/artifact" summary="Returns all artifacts." %} +{% swagger-description %} +Returns all artifacts. +{% endswagger-description %} + +{% swagger-parameter in="query" name="policyID" type="String" required="true" %} +Policy Identifier +{% endswagger-parameter %} + +{% swagger-parameter in="query" name="pageIndex" type="Integer" required="true" %} +The number of pages to skip before starting to collect the result set +{% endswagger-parameter %} + +{% swagger-parameter in="query" name="pageSize" type="Integer" required="true" %} +The numbers of items to return +{% endswagger-parameter %} + +{% swagger-response status="200: OK" description="Successful Operation" %} +```javascript +{ + headers: + x-total-count: + schema: + type: integer + description: Total items in the collection. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Artifact' +} +``` +{% endswagger-response %} + +{% swagger-response status="401: Unauthorized" description="Unauthorized" %} +```javascript +{ + // Response +} +``` +{% endswagger-response %} + +{% swagger-response status="403: Forbidden" description="Forbidden" %} +```javascript +{ + // Response +} +``` +{% endswagger-response %} + +{% swagger-response status="500: Internal Server Error" description="Internal Server Error" %} +```javascript +{ + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +} +``` +{% endswagger-response %} +{% endswagger %} diff --git a/docs/guardian/standard-registry/artifacts/artifacts-apis/upload-artifacts.md b/docs/guardian/standard-registry/artifacts/artifacts-apis/upload-artifacts.md new file mode 100644 index 0000000000..c635750843 --- /dev/null +++ b/docs/guardian/standard-registry/artifacts/artifacts-apis/upload-artifacts.md @@ -0,0 +1,63 @@ +# Upload Artifacts + +{% swagger method="post" path="" baseUrl="/artifact/{policyId}" summary="Upload Artifact" %} +{% swagger-description %} +Upload artifact. For users with the Standard Registry role only. +{% endswagger-description %} + +{% swagger-parameter in="body" name="schema" type="Object" required="true" %} + +{% endswagger-parameter %} + +{% swagger-parameter in="body" name="artifacts" type="Array" required="true" %} + +{% endswagger-parameter %} + +{% swagger-parameter in="body" name="items" type="String" required="true" %} + +{% endswagger-parameter %} + +{% swagger-parameter in="path" name="policyID" type="String" required="true" %} +Policy Identifier +{% endswagger-parameter %} + +{% swagger-response status="200: OK" description="Successful Operation" %} +```javascript +{ + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Artifact' +} +``` +{% endswagger-response %} + +{% swagger-response status="401: Unauthorized" description="Unauthorized" %} +```javascript +{ + // Response +} +``` +{% endswagger-response %} + +{% swagger-response status="403: Forbidden" description="Forbidden" %} +```javascript +{ + // Response +} +``` +{% endswagger-response %} + +{% swagger-response status="500: Internal Server Error" description="Internal Server Error" %} +```javascript +{ + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +} +``` +{% endswagger-response %} +{% endswagger %} diff --git a/docs/guardian/standard-registry/policies/introduction/impactaddon.md b/docs/guardian/standard-registry/policies/introduction/impactaddon.md new file mode 100644 index 0000000000..78902170b8 --- /dev/null +++ b/docs/guardian/standard-registry/policies/introduction/impactaddon.md @@ -0,0 +1,107 @@ +# impactAddon + +This Addon for the mint block which allows to add additional info for the token being created. + +### 1. Properties + +| Property Name | Description | Example | +| ---------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| Type | Type of workflow logic block | impactAddon Block (Can't be changed) | +| GET | API Method to display the data | No | +| POST | API Method to post the data | No | +| Server Input | Server URL to take as an input | | +| Server Output | Server URL to take as an output | | +| Control Type | | special | +| Children | | None | +| Tag | Unique name for the logic block. | impactAddon\_1 | +| Permissions | Which entity has rights to interact at this part of the workflow. | Standard Registry | +| Default Active | Shows whether this block is active at this time and whether it needs to be shown. | Checked or UnChecked | +| Stop Propagation | End processing here, don't pass control to the next block. | Checked or UnChecked | +| On Errors | Called if the system error has occurs in the Block |
  • No action
  • Retry
  • Go to step
  • Go to tag
| +| Impact Type | shows the type of the impact | Primary Impacts / Secondary Impacts | +| Label | Title of the Impact | Test | +| Description | Description of the impact | Impact description | +| Amount (Formula) | Formula for calculating the impact quantitative representation based on the data from the source VC | field0 | +| Unit | Unit of measurement of impact amounts | Kg | + + + +
+ +### 2. Data Format + +Adding impactAddon changes VP documents such that: + +2.1 New VC documents are added for each impact describing it + +``` +{ + "id": "732d46ca-1e19-43a2-a4b1-49cf5ea08aa9", + "type": [ + "VerifiableCredential" + ], + "issuer": "did:hedera:testnet:BJDCUTd8gFSaFwW4w7Tw8dbx7DfnkfLjJ14s2dquesS9_0.0.49039174", + "issuanceDate": "2022-12-06T11:27:37.964Z", + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "credentialSubject": [ + { + "@context": [ + "ipfs://bafkreiabgmqbzd4s2u2jy74ebkawbtvs3ohc76qhphx4vvuymskpfmjj2u" + ], + "type": "ActivityImpact", + "impactType": "Primary Impacts", + "date": "2022-12-06T11:27:37.959Z", + "amount": "2000", + "unit": "kg", + "label": "Test 1", + "description": "Test Description 1" + } + ], + "proof": { + "type": "Ed25519Signature2018", + "created": "2022-12-06T11:27:37Z", + "verificationMethod": "did:hedera:testnet:BJDCUTd8gFSaFwW4w7Tw8dbx7DfnkfLjJ14s2dquesS9_0.0.49039174#did-root-key", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..nK_fvwC9nW9jtKHm2U4yAHhIcgpGCkr1H7PiPNwKzAj9gW2sblpu0TAQEow9OR9v1aREEH-fFLmyAGEfXWfXAw" + } +} + +``` + +2.2 All source VCs are substituted for a single cumulative VC containing links to the source (original) VCs. + +``` +{ + "id": "443e9e7b-3a67-4ad9-a22d-d85c2d28562f", + "type": [ + "VerifiableCredential" + ], + "issuer": "did:hedera:testnet:BJDCUTd8gFSaFwW4w7Tw8dbx7DfnkfLjJ14s2dquesS9_0.0.49039174", + "issuanceDate": "2022-12-06T11:27:37.936Z", + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "credentialSubject": [ + { + "@context": [ + "ipfs://bafkreicnjditzstltfyu2327pqqcstuwl5vegwe2hepbwcque2gjvwsm3y" + ], + "type": "TokenDataSource", + "dataSource": [ + "1670.326057800", + "1670.451857512" + ] + } + ], + "proof": { + "type": "Ed25519Signature2018", + "created": "2022-12-06T11:27:37Z", + "verificationMethod": "did:hedera:testnet:BJDCUTd8gFSaFwW4w7Tw8dbx7DfnkfLjJ14s2dquesS9_0.0.49039174#did-root-key", + "proofPurpose": "assertionMethod", + "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..N5sOQOgxBxPdvGvCJbfJ4rWBc6374Ht96xKTAN6Pwrhr8Pk4SWqS6IwDePBySTIbvVETPFFxjzGQZMsbENx5BA" + } +} + +``` diff --git a/docs/guardian/standard-registry/policies/page-1.md b/docs/guardian/standard-registry/policies/page-1.md index 12cac7482e..a452917a03 100644 --- a/docs/guardian/standard-registry/policies/page-1.md +++ b/docs/guardian/standard-registry/policies/page-1.md @@ -21,11 +21,11 @@ Step 1: Need to click on the linking icon for the policy, which you wanted to be Step 2: We get a policy linking pop up to create a link for the primary policy or joining an existing policy: -
+
After the β€˜primary’ policy is registered a special β€˜link’ becomes accessible which can be used to β€˜connect’ additional β€˜secondary’ policies. -
+
### 2. Connecting Secondary Policies diff --git a/docs/guardian/standard-registry/schemas/creating-system-schema-using-ui.md b/docs/guardian/standard-registry/schemas/creating-system-schema-using-ui.md index dc43d8b69b..dc3e490060 100644 --- a/docs/guardian/standard-registry/schemas/creating-system-schema-using-ui.md +++ b/docs/guardian/standard-registry/schemas/creating-system-schema-using-ui.md @@ -61,4 +61,4 @@ Once the above details are added, click on the Create button. Once the System Schema is created, we have options for activating, deleting, editing and viewing JSON documents. -![](<../../../.gitbook/assets/image (9) (3).png>) +![](<../../../.gitbook/assets/image (9) (3) (1).png>) diff --git a/docs/policy-flow/policy-demo.md b/docs/policy-flow/policy-demo.md index 1f35cacedc..30c83098bc 100644 --- a/docs/policy-flow/policy-demo.md +++ b/docs/policy-flow/policy-demo.md @@ -25,7 +25,7 @@ We have two options to create Policy : 1.4 You can even edit the Policy by clicking Edit button. -![](<../.gitbook/assets/image (2) (1) (1).png>) +![](<../.gitbook/assets/image (2) (1) (1) (1).png>) 1.5 When clicked on Edit, the screen is navigated to Policies configuration tab, which gives an option to add/remove any block from the Policy. diff --git a/frontend/package.json b/frontend/package.json index 33d5b688bd..ce5ae1f957 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -61,5 +61,6 @@ "test": "ng test", "watch": "ng build --watch --configuration development --output-path ../www-data" }, - "version": "2.7.0" + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0" } diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index dd96fad6a5..1bdc165bd5 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -23,6 +23,8 @@ import { InfoComponent } from './components/info/info/info.component'; import { WebSocketService } from './services/web-socket.service'; import { PoliciesComponent } from './policy-engine/policies/policies.component'; import { ArtifactConfigComponent } from './artifact-engine/artifact-config/artifact-config.component'; +import { ContractConfigComponent } from './views/contract-config/contract-config.component'; +import { ContractRequestConfigComponent } from './views/contract-request-config/contract-request-config.component'; const USER_IS_NOT_RA = "Page is avaliable for admin only"; @@ -147,6 +149,8 @@ const routes: Routes = [ { path: 'config', component: RootConfigComponent, canActivate: [StandardRegistryGuard, ServicesStatusGuard] }, { path: 'tokens', component: TokenConfigComponent, canActivate: [StandardRegistryGuard, ServicesStatusGuard] }, + { path: 'contracts', component: ContractConfigComponent, canActivate: [StandardRegistryGuard, ServicesStatusGuard] }, + { path: 'contracts/pairs', component: ContractRequestConfigComponent, canActivate: [StandardRegistryGuard, ServicesStatusGuard] }, { path: 'schemas', component: SchemaConfigComponent, canActivate: [StandardRegistryGuard, ServicesStatusGuard] }, { path: 'artifacts', component: ArtifactConfigComponent, canActivate: [StandardRegistryGuard, ServicesStatusGuard] }, { diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 15aaae3ef9..0d9e7f4779 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -46,6 +46,12 @@ import { WebSocketService } from './services/web-socket.service'; import { MessageTranslationService } from './services/message-translation-service/message-translation-service'; import { TasksService } from './services/tasks.service'; import { ArtifactService } from './services/artifact.service'; +import { ContractConfigComponent } from './views/contract-config/contract-config.component'; +import { ContractService } from './services/contract.service'; +import { ContractRequestConfigComponent } from './views/contract-request-config/contract-request-config.component'; +import { AddPairDialogComponent } from './components/add-pair-dialog/add-pair-dialog.component'; +import { RetireTokenDialogComponent } from './components/retire-token-dialog/retire-token-dialog.component'; +import { DataInputDialogComponent } from './components/data-input-dialog/data-input-dialog.component'; @NgModule({ declarations: [ @@ -69,7 +75,12 @@ import { ArtifactService } from './services/artifact.service'; DetailsLogDialog, ServiceStatusComponent, ConfirmationDialogComponent, - InfoComponent + InfoComponent, + ContractConfigComponent, + ContractRequestConfigComponent, + AddPairDialogComponent, + RetireTokenDialogComponent, + DataInputDialogComponent, ], imports: [ BrowserModule, @@ -103,6 +114,7 @@ import { ArtifactService } from './services/artifact.service'; DemoService, MessageTranslationService, TasksService, + ContractService, { provide: HTTP_INTERCEPTORS, useClass: HandleErrorsService, diff --git a/frontend/src/app/common-components.module.ts b/frontend/src/app/common-components.module.ts index 8a6fadba7f..5718fd2010 100644 --- a/frontend/src/app/common-components.module.ts +++ b/frontend/src/app/common-components.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MaterialModule } from './material.module'; import { DatetimePicker } from './components/datetime-picker/datetime-picker.component'; -import { Dragonglass } from './components/dragonglass/dragonglass.component'; +import { HederaExplorer } from './components/hedera-explorer/hedera-explorer.component'; import { NgxMatDateFormats, NgxMatDatetimePickerModule, NGX_MAT_DATE_FORMATS } from '@angular-material-components/datetime-picker'; import { SelectMenuButton } from './components/select-menu/select-menu.component'; import { AsyncProgessComponent } from './components/async-progess/async-progess.component'; @@ -12,7 +12,7 @@ import { FormsModule } from '@angular/forms'; @NgModule({ declarations: [ DatetimePicker, - Dragonglass, + HederaExplorer, SelectMenuButton, AsyncProgessComponent, SwitchButton @@ -25,10 +25,10 @@ import { FormsModule } from '@angular/forms'; ], exports: [ DatetimePicker, - Dragonglass, + HederaExplorer, SelectMenuButton, SwitchButton, AsyncProgessComponent ] }) -export class CommonComponentsModule { } \ No newline at end of file +export class CommonComponentsModule { } diff --git a/frontend/src/app/components/add-pair-dialog/add-pair-dialog.component.css b/frontend/src/app/components/add-pair-dialog/add-pair-dialog.component.css new file mode 100644 index 0000000000..61117ebaf2 --- /dev/null +++ b/frontend/src/app/components/add-pair-dialog/add-pair-dialog.component.css @@ -0,0 +1,123 @@ +.mat-dialog-content { + overflow: visible; +} + +.mat-dialog-actions { + padding: 15px 0; +} + +.description { + margin-bottom: 20px; +} + +.g-dialog-actions-btn { + min-width: 210px !important; +} + +.contract-form-base-token-count, +.contract-form-opposite-token-count { + padding-top: 12px; + width: 50%; +} + +.contract-form-base-token mat-icon, +.contract-form-base-token-count mat-icon, +.contract-form-opposite-token mat-icon, +.contract-form-opposite-token-count mat-icon { + color: #3f51b5; +} + +.contract-form-base-token { + width: 100%; +} + +.contract-form-opposite-token { + width: 100%; +} + +.delimiter { + width: 100%; + height: 40px; +} + +.contract-info { + text-align: center; +} + + +.block-delimiter { + width: 100%; + height: 100px; + box-sizing: border-box; + position: relative; +} + +.block-delimiter::after { + content: attr(data-label); + position: absolute; + color: #646464; + font-weight: 500; + font-size: 16px; + padding: 10px; + background: #fff; + left: 50%; + top: 30px; + transform: translate(-50%, 0px); +} + +.block-delimiter::before { + content: ''; + position: absolute; + height: 2px; + top: 50px; + left: 0px; + right: 0px; + border-top: 2px solid #ababab; +} + +.contract-from-token-info { + display: flex; + align-items: center; + gap: 10px; +} + +.contract-from-token-info .contract-from-token-info-label { + color: #646464; + font-weight: 500; + font-size: 16px; +} + +.contract-from-token-info mat-icon { + color: #3f51b5; + font-size: 40px; + width: 40px; + height: 40px; +} + +.contract-form-base-token-count-container { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 0 0 30px; +} + +.pair-loading { + margin: 0 auto; +} + +.exists-pairs { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + color: darkgoldenrod; + margin-bottom: 20px; + gap: 20px; +} + +.exists-pairs-warn-info { + display: flex; + justify-content: center; + align-items: center; + gap: 10px; +} \ No newline at end of file diff --git a/frontend/src/app/components/add-pair-dialog/add-pair-dialog.component.html b/frontend/src/app/components/add-pair-dialog/add-pair-dialog.component.html new file mode 100644 index 0000000000..83975827b6 --- /dev/null +++ b/frontend/src/app/components/add-pair-dialog/add-pair-dialog.component.html @@ -0,0 +1,73 @@ +
+
+
+
+ close +
+
+
+ Create Pair +
+
+
+ Create +
+
+
+
+
+
+
warning +
Pair already exists in {{existsPairs.length}} contract[s]:
+
+
{{existsPairs.join(", ")}}
+
+
{{this.dataForm.value?.baseTokenCount || 0}} token[s] + {{this.dataForm.value?.baseTokenId || '...'}} will be retired by + {{this.dataForm.value?.oppositeTokenCount || 0}} token[s] {{this.dataForm.value?.oppositeTokenId || + '...'}} +
+ +
+
+ + Token + + + {{token.tokenName}} + + +
+
generating_tokens +
{{dataForm.value?.baseTokenId || '...'}} Token
+
+ + account_balance_wallet + + +
+ +
+ + Token + + + {{token.tokenName}} + + +
+
generating_tokens +
{{dataForm.value?.oppositeTokenId || '...'}} Token +
+
+ + account_balance_wallet + + +
+
+
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/add-pair-dialog/add-pair-dialog.component.ts b/frontend/src/app/components/add-pair-dialog/add-pair-dialog.component.ts new file mode 100644 index 0000000000..541bca1479 --- /dev/null +++ b/frontend/src/app/components/add-pair-dialog/add-pair-dialog.component.ts @@ -0,0 +1,132 @@ +import { Component, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { + AbstractControl, + FormBuilder, + ValidationErrors, + ValidatorFn, + Validators, +} from '@angular/forms'; +import { ContractService } from 'src/app/services/contract.service'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; +/** + * Dialog for creating pair. + */ +@Component({ + selector: 'add-pair-dialog', + templateUrl: './add-pair-dialog.component.html', + styleUrls: ['./add-pair-dialog.component.css'], +}) +export class AddPairDialogComponent { + dataForm = this.fb.group({ + baseTokenId: ['', Validators.required], + baseTokenCount: [0, this.moreThanZeroValidator()], + oppositeTokenId: ['', Validators.required], + oppositeTokenCount: [0, this.moreThanZeroValidator()], + contractId: ['', Validators.required], + }); + loading: boolean = false; + existsPairs: any = []; + tokens: any[] = []; + baseTokens: any[] = []; + oppositeTokens: any[] = []; + destroy$: Subject = new Subject(); + + constructor( + public dialogRef: MatDialogRef, + private contractService: ContractService, + private fb: FormBuilder, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + this.tokens = data?.tokens || []; + this.baseTokens = this.tokens; + this.oppositeTokens = this.tokens; + this.dataForm.get('contractId')?.patchValue(data?.contractId || ''); + this.dataForm + .get('baseTokenId') + ?.valueChanges.pipe(takeUntil(this.destroy$)) + .subscribe(this.onTokenChange('oppositeTokenId')); + this.dataForm + .get('oppositeTokenId') + ?.valueChanges.pipe(takeUntil(this.destroy$)) + .subscribe(this.onTokenChange('baseTokenId', true)); + } + + onTokenChange(controlName: string, isOppositeToken: boolean = false) { + return (value: any) => { + const tokenId = this.dataForm.get(controlName)?.value; + if (isOppositeToken) { + this.baseTokens = this.tokens.filter(item => item.tokenId !== value) + } else { + this.oppositeTokens = this.tokens.filter(item => item.tokenId !== value); + } + this.existsPairs = []; + if (!value || !tokenId) { + return; + } + this.loading = true; + this.contractService + .getPair( + isOppositeToken ? tokenId : value, + isOppositeToken ? value : tokenId + ) + .subscribe( + (result) => { + this.loading = false; + if (!result) { + return; + } + this.existsPairs = result + .filter( + (item: any) => + item.baseTokenRate && item.oppositeTokenRate + ) + .map( + (item: any) => + `${item.contractId} (${item.baseTokenRate}:${item.oppositeTokenRate})` + ); + }, + () => (this.loading = false) + ); + }; + } + + ngOnInit() {} + + onNoClick(): void { + this.dialogRef.close(null); + } + + onCreate() { + if (this.dataForm.valid) { + this.dialogRef.close(this.dataForm.value); + } + } + + onSave() { + if (this.dataForm.valid) { + const data = this.dataForm.value; + this.dialogRef.close(data); + } + } + + ngOnDestroy() { + this.destroy$.next(true); + this.destroy$.unsubscribe(); + } + + moreThanZeroValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value = control.value; + if (typeof value === 'number' && value > 0) { + return null; + } + return { + lessThanZero: { + valid: false, + }, + }; + }; + } +} diff --git a/frontend/src/app/components/data-input-dialog/data-input-dialog.component.css b/frontend/src/app/components/data-input-dialog/data-input-dialog.component.css new file mode 100644 index 0000000000..9dc919469d --- /dev/null +++ b/frontend/src/app/components/data-input-dialog/data-input-dialog.component.css @@ -0,0 +1,27 @@ +form { + display: flex; + flex-direction: column; + width: 452px; + overflow: visible; + padding-bottom: 25px; +} + +.content { + overflow: visible; + position: relative; +} + +.loading { + display: flex; + align-items: center; + justify-items: center; + justify-content: center; + align-content: center; + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + background: #fff; + z-index: 1; +} \ No newline at end of file diff --git a/frontend/src/app/components/data-input-dialog/data-input-dialog.component.html b/frontend/src/app/components/data-input-dialog/data-input-dialog.component.html new file mode 100644 index 0000000000..5e9500aa6f --- /dev/null +++ b/frontend/src/app/components/data-input-dialog/data-input-dialog.component.html @@ -0,0 +1,22 @@ +
+

+ {{title}} +

+
+
+ + {{field.label}} + + +
+ + +
+
+
+ +
+ +
+
\ No newline at end of file diff --git a/frontend/src/app/components/data-input-dialog/data-input-dialog.component.ts b/frontend/src/app/components/data-input-dialog/data-input-dialog.component.ts new file mode 100644 index 0000000000..aaa5a26355 --- /dev/null +++ b/frontend/src/app/components/data-input-dialog/data-input-dialog.component.ts @@ -0,0 +1,43 @@ +import { Component, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; + +/** + * Dialog for creating policy. + */ +@Component({ + selector: 'data-input-dialog', + templateUrl: './data-input-dialog.component.html', + styleUrls: ['./data-input-dialog.component.css'], +}) +export class DataInputDialogComponent { + dataForm = new FormGroup({}); + loading: boolean = false; + + title: string = ''; + fieldsConfig: any = []; + + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + if (!data) { + return; + } + this.title = data.title; + this.fieldsConfig = data.fieldsConfig; + this.fieldsConfig.forEach((item: any) => { + this.dataForm.addControl( + item.name, + new FormControl( + item.initialValue, + item.required ? Validators.required : null + ) + ); + }); + } + + onNoClick(): void { + this.dialogRef.close(null); + } +} \ No newline at end of file diff --git a/frontend/src/app/components/dragonglass/dragonglass.component.html b/frontend/src/app/components/dragonglass/dragonglass.component.html deleted file mode 100644 index 896c20dbde..0000000000 --- a/frontend/src/app/components/dragonglass/dragonglass.component.html +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/src/app/components/dragonglass/dragonglass.component.ts b/frontend/src/app/components/dragonglass/dragonglass.component.ts deleted file mode 100644 index 8c1f8af520..0000000000 --- a/frontend/src/app/components/dragonglass/dragonglass.component.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Component, Inject, Input, SimpleChanges } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { SettingsService } from 'src/app/services/settings.service'; - -/** - * Dialog for icon preview. - */ -@Component({ - selector: 'dragonglass', - templateUrl: './dragonglass.component.html', - styleUrls: ['./dragonglass.component.css'] -}) -export class Dragonglass { - url: string; - - @Input('type') type!: string; - @Input('params') params!: string | null; - - constructor(private settingsService: SettingsService) { - this.url = ''; - } - - ngOnChanges(changes: SimpleChanges): void { - this.settingsService.getHederaNet().subscribe((res: string) => { - const urlPrefix = res == 'mainnet' ? 'app' : 'testnet'; - - switch (this.type) { - case 'topics': - this.url = `https://${urlPrefix}.dragonglass.me/hedera/topics/${this.params}`; - break; - case 'tokens': - this.url = `https://${urlPrefix}.dragonglass.me/hedera/tokens/${this.params}`; - break; - case 'accounts': - this.url = `https://${urlPrefix}.dragonglass.me/hedera/accounts/${this.params}`; - break; - default: - this.url = ''; - break; - } - }); - } -} \ No newline at end of file diff --git a/frontend/src/app/components/dragonglass/dragonglass.component.css b/frontend/src/app/components/hedera-explorer/hedera-explorer.component.css similarity index 100% rename from frontend/src/app/components/dragonglass/dragonglass.component.css rename to frontend/src/app/components/hedera-explorer/hedera-explorer.component.css diff --git a/frontend/src/app/components/hedera-explorer/hedera-explorer.component.html b/frontend/src/app/components/hedera-explorer/hedera-explorer.component.html new file mode 100644 index 0000000000..dfd727e0f5 --- /dev/null +++ b/frontend/src/app/components/hedera-explorer/hedera-explorer.component.html @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/app/components/hedera-explorer/hedera-explorer.component.ts b/frontend/src/app/components/hedera-explorer/hedera-explorer.component.ts new file mode 100644 index 0000000000..36249602a4 --- /dev/null +++ b/frontend/src/app/components/hedera-explorer/hedera-explorer.component.ts @@ -0,0 +1,33 @@ +import { Component, Input, SimpleChanges } from '@angular/core'; +import { SettingsService } from 'src/app/services/settings.service'; +import { environment } from 'src/environments/environment'; + +/** + * Hedera explorer. + */ +@Component({ + selector: 'hedera-explorer', + templateUrl: './hedera-explorer.component.html', + styleUrls: ['./hedera-explorer.component.css'] +}) +export class HederaExplorer { + url: string; + + @Input('type') type!: string; + @Input('params') params!: string | null; + + constructor(private settingsService: SettingsService) { + this.url = ''; + } + + ngOnChanges(changes: SimpleChanges): void { + const networkMap: any = environment.explorerSettings.networkMap; + const typeMap: any = environment.explorerSettings.typeMap; + this.settingsService.getHederaNet().subscribe((res: string) => { + this.url = environment.explorerSettings.url + .replace('${network}', networkMap[res] || '') + .replace('${type}', typeMap[this.type] || '') + .replace('${value}', this.params || ''); + }); + } +} diff --git a/frontend/src/app/components/retire-token-dialog/retire-token-dialog.component.css b/frontend/src/app/components/retire-token-dialog/retire-token-dialog.component.css new file mode 100644 index 0000000000..28eaf897dd --- /dev/null +++ b/frontend/src/app/components/retire-token-dialog/retire-token-dialog.component.css @@ -0,0 +1,119 @@ +.mat-dialog-content { + overflow: visible; +} + +.mat-dialog-actions { + padding: 15px 0; +} + +.description { + margin-bottom: 20px; +} + +.g-dialog-actions-btn { + min-width: 210px !important; +} + +.contract-form-base-token-count, +.contract-form-opposite-token-count { + padding-top: 12px; + width: 100%; +} + +.contract-form-base-token mat-icon, +.contract-form-base-token-count mat-icon, +.contract-form-opposite-token mat-icon, +.contract-form-opposite-token-count mat-icon { + color: #3f51b5; +} + +.contract-form-base-token { + width: 100%; +} + +.contract-form-opposite-token { + width: 100%; +} + +.delimiter { + width: 100%; + height: 40px; +} + +.contract-info { + text-align: center; +} + +.block-delimiter { + width: 100%; + height: 100px; + box-sizing: border-box; + position: relative; +} + +.block-delimiter::after { + content: attr(data-label); + position: absolute; + color: #646464; + font-weight: 500; + font-size: 16px; + padding: 10px; + background: #fff; + left: 50%; + top: 30px; + transform: translate(-50%, 0px); +} + +.block-delimiter::before { + content: ''; + position: absolute; + height: 2px; + top: 50px; + left: 0px; + right: 0px; + border-top: 2px solid #ababab; +} + +:host .mat-accent .mat-slider-track-fill { + background-color: red !important; +} + +:host .mat-slider-thumb-label { + background-color: red !important; + +} + +:host .mat-slider-thumb { + background-color: red !important; +} + +.pair-rate { + font-size: 30px; + margin: 20px; +} + +.contracts-spinner { + margin: 0 auto; +} + +:host ::ng-deep mat-step-header { + pointer-events: unset !important; + cursor: pointer; + background: unset !important; +} + +.step-action-buttons { + display: flex; + justify-content: space-between; + align-items: center; + gap: 30px; + margin-top: 25px; +} + +.step-action-buttons button { + flex: 1; +} + +.step-action-prev-btn { + border: 1px solid rgba(0, 0, 0, .12); +} \ No newline at end of file diff --git a/frontend/src/app/components/retire-token-dialog/retire-token-dialog.component.html b/frontend/src/app/components/retire-token-dialog/retire-token-dialog.component.html new file mode 100644 index 0000000000..f99d7974f4 --- /dev/null +++ b/frontend/src/app/components/retire-token-dialog/retire-token-dialog.component.html @@ -0,0 +1,123 @@ +
+
+
+
+ close +
+
+
+ Create Retire Request +
+
+
+ Create +
+
+
+
+
+ + +
+ Choose token pair + + generating_tokens + + + {{token.tokenName}} + + + + generating_tokens + + + {{token.tokenName}} + + +
+
+ +
+
+ + Choose contract + + contract + + + {{contract.contractId}} - + {{contract.baseTokenRate}}:{{contract.oppositeTokenRate}} + + + +
+ + +
+
+ +
+
Choose tokens to find available pairs
+
Ratio {{baseTokenRate}} : + {{oppositeTokenRate}}
+
+ Set count +
+
+ + Base Token Serials + + + {{tokenCountForm.value?.baseTokenSerials ? tokenCountForm.value?.baseTokenSerials[0] + : ''}} + + (+{{tokenCountForm.value?.baseTokenSerials.length - 1}} + {{tokenCountForm.value?.baseTokenSerials.length === 2 ? 'other' : 'others'}}) + + + {{serial}} + + + + account_balance_wallet + + +
+ + Opposite Token Serials + + + {{tokenCountForm.value?.oppositeTokenSerials ? + tokenCountForm.value?.oppositeTokenSerials[0] : ''}} + + (+{{tokenCountForm.value?.oppositeTokenSerials.length - 1}} + {{tokenCountForm.value?.oppositeTokenSerials.length === 2 ? 'other' : + 'others'}}) + + + {{serial}} + + + + account_balance_wallet + + +
+
+
+
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/retire-token-dialog/retire-token-dialog.component.ts b/frontend/src/app/components/retire-token-dialog/retire-token-dialog.component.ts new file mode 100644 index 0000000000..be43f13e31 --- /dev/null +++ b/frontend/src/app/components/retire-token-dialog/retire-token-dialog.component.ts @@ -0,0 +1,252 @@ +import { Component, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { FormBuilder, Validators } from '@angular/forms'; +import { ContractService } from 'src/app/services/contract.service'; +import { STEPPER_GLOBAL_OPTIONS } from '@angular/cdk/stepper'; +import { COMMA, ENTER } from '@angular/cdk/keycodes'; +import { TokenType } from '@guardian/interfaces'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +/** + * Dialog for retire tokens. + */ +@Component({ + selector: 'retire-token-dialog', + templateUrl: './retire-token-dialog.component.html', + styleUrls: ['./retire-token-dialog.component.css'], + providers: [ + { + provide: STEPPER_GLOBAL_OPTIONS, + useValue: { showError: true }, + }, + ], +}) +export class RetireTokenDialogComponent { + tokens: any[] = []; + baseTokens: any[] = []; + oppositeTokens: any = []; + + chooseTokensForm = this.fb.group({ + baseTokenId: ['', Validators.required], + oppositeTokenId: ['', Validators.required], + }); + contractForm = this.fb.control('', Validators.required); + tokenCountForm = this.fb.group({ + baseTokenCount: [0], + oppositeTokenCount: [0], + baseTokenSerials: [[]], + oppositeTokenSerials: [[]], + }); + + baseTokenType?: TokenType; + baseTokenDecimals?: number; + baseTokenSerials: any = []; + baseTokenRate: number = 0; + oppositeTokenType?: TokenType; + oppositeTokenDecimals?: number; + oppositeTokenSerials: any = []; + oppositeTokenRate: number = 0; + + contractAndRates: any = []; + contractsLoading = false; + + readonly separatorKeysCodes = [ENTER, COMMA] as const; + + destroy$: Subject = new Subject(); + + constructor( + public dialogRef: MatDialogRef, + private fb: FormBuilder, + private contractService: ContractService, + @Inject(MAT_DIALOG_DATA) public data: any + ) { + if (data) { + this.tokens = data.tokens || []; + this.baseTokens = this.tokens; + this.oppositeTokens = this.tokens; + const baseTokenControl = this.chooseTokensForm.get('baseTokenId'); + const oppositeTokenControl = + this.chooseTokensForm.get('oppositeTokenId'); + const oppositeTokenCountControl = + this.tokenCountForm.get('oppositeTokenCount'); + const baseTokenCountControl = + this.tokenCountForm.get('baseTokenCount'); + + oppositeTokenCountControl?.disable(); + baseTokenControl?.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe((value) => { + this.oppositeTokens = this.tokens.filter( + (item) => item.tokenId != value + ); + const baseToken = this.tokens.find( + (item: any) => item.tokenId === value + ); + this.baseTokenType = baseToken?.tokenType; + this.baseTokenDecimals = baseToken?.decimals; + this.baseTokenSerials = baseToken?.serials || []; + const oppositeTokenId = + this.chooseTokensForm.get('oppositeTokenId')?.value; + this.contractAndRates = []; + this.contractForm.patchValue(''); + this.tokenCountForm.reset(); + this.setContractPair(value, oppositeTokenId); + }); + this.contractForm.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe((value) => { + this.tokenCountForm.reset({ + baseTokenCount: 0, + oppositeTokenCount: 0, + baseTokenSerials: [], + oppositeTokenSerials: [], + }); + if (!value) { + this.baseTokenRate = 0; + this.oppositeTokenRate = 0; + return; + } + const contractAndRates = this.contractAndRates.find( + (item: any) => item.contractId === value + ); + this.baseTokenRate = contractAndRates.baseTokenRate; + this.oppositeTokenRate = contractAndRates.oppositeTokenRate; + }); + oppositeTokenControl?.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe((value) => { + this.baseTokens = this.tokens.filter( + (item) => item.tokenId != value + ); + const oppositeToken = this.tokens.find( + (item: any) => item.tokenId === value + ); + this.oppositeTokenType = oppositeToken?.tokenType; + this.oppositeTokenDecimals = oppositeToken?.decimals; + this.oppositeTokenSerials = oppositeToken?.serials || []; + const baseTokenId = + this.chooseTokensForm.get('baseTokenId')?.value; + this.contractAndRates = []; + this.contractForm.patchValue(''); + this.tokenCountForm.reset(); + this.setContractPair(baseTokenId, value); + }); + + baseTokenCountControl?.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(this.countValueForOppositeToken.bind(this)); + } + } + + setContractPair(baseTokenId: string, oppositeTokenId: string) { + if (!baseTokenId || !oppositeTokenId) { + return; + } + this.contractsLoading = true; + this.contractService.getPair(baseTokenId, oppositeTokenId).subscribe( + (result) => { + if (!result) { + this.contractsLoading = false; + return; + } + this.contractAndRates = result.filter( + (item: any) => item.baseTokenRate && item.oppositeTokenRate + ); + this.contractsLoading = false; + }, + () => (this.contractsLoading = false) + ); + } + + countValueForOppositeToken(baseTokenValue: number) { + if (!this.tokenCountForm) { + return; + } + const oppositeTokenControl = + this.tokenCountForm.get('oppositeTokenCount'); + if ( + this.oppositeTokenType == TokenType.FUNGIBLE && + this.baseTokenRate && + this.oppositeTokenRate && + baseTokenValue >= this.baseTokenRate + ) { + const baseTokenCount = this.baseTokenDecimals + ? baseTokenValue * Math.pow(10, this.baseTokenDecimals) + : baseTokenValue; + const baseTokenRate = this.baseTokenDecimals + ? this.baseTokenRate * Math.pow(10, this.baseTokenDecimals) + : this.baseTokenRate; + if (baseTokenCount % baseTokenRate === 0) { + const rate = baseTokenCount / baseTokenRate; + oppositeTokenControl?.patchValue(rate * this.oppositeTokenRate); + } else { + oppositeTokenControl?.patchValue(0); + } + } else { + oppositeTokenControl?.patchValue(0); + } + } + + ngOnInit() {} + + onNoClick(): void { + this.dialogRef.close(null); + } + + onCreate() { + this.dialogRef.close({ + ...this.chooseTokensForm.value, + ...this.tokenCountForm.getRawValue(), + contractId: this.contractForm.value, + }); + } + + validateCountValues() { + if (!this.baseTokenRate || !this.oppositeTokenRate) { + return false; + } + + const { + baseTokenCount, + oppositeTokenCount, + baseTokenSerials, + oppositeTokenSerials, + } = this.tokenCountForm.getRawValue() || {}; + + if ( + !baseTokenCount && + !oppositeTokenCount && + !baseTokenSerials?.length && + !oppositeTokenSerials?.length + ) { + return false; + } + + const baseTokenSerialsCount = baseTokenSerials.length; + const oppositeTokenSerialsCount = oppositeTokenSerials.length; + + const baseTokenCountWithDecimals = this.baseTokenDecimals + ? (baseTokenCount || baseTokenSerialsCount) * + Math.pow(10, this.baseTokenDecimals) + : baseTokenCount || baseTokenSerialsCount; + const baseTokenRate = this.baseTokenDecimals + ? this.baseTokenRate * Math.pow(10, this.baseTokenDecimals) + : this.baseTokenRate; + if (baseTokenCountWithDecimals % baseTokenRate === 0) { + const rate = baseTokenCountWithDecimals / baseTokenRate; + if ( + rate * this.oppositeTokenRate === + (oppositeTokenSerialsCount || oppositeTokenCount) + ) { + return true; + } + } + return false; + } + + ngOnDestroy() { + this.destroy$.next(true); + this.destroy$.unsubscribe(); + } +} \ No newline at end of file diff --git a/frontend/src/app/material.module.ts b/frontend/src/app/material.module.ts index 64cc6e7e1f..9d6d8016c7 100644 --- a/frontend/src/app/material.module.ts +++ b/frontend/src/app/material.module.ts @@ -30,6 +30,7 @@ import { MatChipsModule } from '@angular/material/chips'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { DragDropModule } from '@angular/cdk/drag-drop'; +import { MatSliderModule } from '@angular/material/slider'; @NgModule({ declarations: [], @@ -60,7 +61,8 @@ import { DragDropModule } from '@angular/cdk/drag-drop'; MatSlideToggleModule, MatPaginatorModule, MatButtonToggleModule, - DragDropModule + DragDropModule, + MatSliderModule, ], exports: [ MatTabsModule, @@ -92,7 +94,8 @@ import { DragDropModule } from '@angular/cdk/drag-drop'; MatChipsModule, MatAutocompleteModule, MatButtonToggleModule, - DragDropModule + DragDropModule, + MatSliderModule, ] }) export class MaterialModule { } \ No newline at end of file diff --git a/frontend/src/app/policy-engine/helpers/code-editor-dialog/code-editor-dialog.component.ts b/frontend/src/app/policy-engine/helpers/code-editor-dialog/code-editor-dialog.component.ts index 28833d400e..14a50b06dc 100644 --- a/frontend/src/app/policy-engine/helpers/code-editor-dialog/code-editor-dialog.component.ts +++ b/frontend/src/app/policy-engine/helpers/code-editor-dialog/code-editor-dialog.component.ts @@ -45,6 +45,9 @@ export class CodeEditorDialogComponent implements OnInit, AfterContentInit { } ngOnInit() { + if (this.data.mode) { + this.codeMirrorOptions.mode = this.data.mode; + } this.expression = this.data.expression; this.codeMirrorOptions.readOnly = this.data.readonly; } diff --git a/frontend/src/app/policy-engine/helpers/tree-node-actions/tree-node-actions.component.html b/frontend/src/app/policy-engine/helpers/tree-node-actions/tree-node-actions.component.html index 798b537fd3..0823df4481 100644 --- a/frontend/src/app/policy-engine/helpers/tree-node-actions/tree-node-actions.component.html +++ b/frontend/src/app/policy-engine/helpers/tree-node-actions/tree-node-actions.component.html @@ -22,7 +22,7 @@ mat-icon-button class="action-button"> more_horiz - diff --git a/frontend/src/app/policy-engine/policies/policies.component.html b/frontend/src/app/policy-engine/policies/policies.component.html index 3501c6e504..e9683d09c3 100644 --- a/frontend/src/app/policy-engine/policies/policies.component.html +++ b/frontend/src/app/policy-engine/policies/policies.component.html @@ -26,7 +26,7 @@ Hedera Topic Id - {{element.topicId}} + {{element.topicId}} @@ -104,13 +104,13 @@
-
share
@@ -157,4 +157,4 @@
- \ No newline at end of file + diff --git a/frontend/src/app/policy-engine/policy-configuration/blocks/main/http-request-config/http-request-config.component.css b/frontend/src/app/policy-engine/policy-configuration/blocks/main/http-request-config/http-request-config.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/app/policy-engine/policy-configuration/blocks/main/http-request-config/http-request-config.component.html b/frontend/src/app/policy-engine/policy-configuration/blocks/main/http-request-config/http-request-config.component.html new file mode 100644 index 0000000000..e93b1b5414 --- /dev/null +++ b/frontend/src/app/policy-engine/policy-configuration/blocks/main/http-request-config/http-request-config.component.html @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + expand_more + + Options
URL + +
Method + + GET + POST + PUT + PATCH + DELETE + +
Body + +
+ + expand_more + + Headers +
+ add + Add header +
+
+ + expand_more + + Header {{i}} + + delete + +
Header name + +
Header value + +
diff --git a/frontend/src/app/policy-engine/policy-configuration/blocks/main/http-request-config/http-request-config.component.ts b/frontend/src/app/policy-engine/policy-configuration/blocks/main/http-request-config/http-request-config.component.ts new file mode 100644 index 0000000000..e495289968 --- /dev/null +++ b/frontend/src/app/policy-engine/policy-configuration/blocks/main/http-request-config/http-request-config.component.ts @@ -0,0 +1,94 @@ +import { Component, EventEmitter, Input, OnInit, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { Schema, Token } from '@guardian/interfaces'; +import { PolicyBlockModel, PolicyModel } from 'src/app/policy-engine/structures/policy-model'; +import { RegisteredBlocks } from 'src/app/policy-engine/registered-blocks'; +import { BlockNode } from '../../../../helpers/tree-data-source/tree-data-source'; +import { CodeEditorDialogComponent } from '../../../../helpers/code-editor-dialog/code-editor-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; + +/** + * Settings for block of 'switch' and 'interfaceStepBlock' types. + */ +@Component({ + selector: 'http-request-config', + templateUrl: './http-request-config.component.html', + styleUrls: ['./http-request-config.component.css'], + encapsulation: ViewEncapsulation.Emulated +}) +export class HttpRequestConfigComponent implements OnInit { + @Input('policy') policy!: PolicyModel; + @Input('block') currentBlock!: PolicyBlockModel; + @Input('schemas') schemas!: Schema[]; + @Input('tokens') tokens!: Token[]; + @Input('readonly') readonly!: boolean; + @Output() onInit = new EventEmitter(); + + propHidden: any = { + main: false, + options: false, + conditionsGroup: false, + conditions: {}, + }; + + block!: any; + + constructor( + public registeredBlocks: RegisteredBlocks, + private dialog: MatDialog + ) { + } + + ngOnInit(): void { + this.onInit.emit(this); + this.load(this.currentBlock); + } + + ngOnChanges(changes: SimpleChanges) { + this.load(this.currentBlock); + } + + load(block: PolicyBlockModel) { + console.log(this); + this.block = block.properties; + this.block.headers = this.block.headers || []; + } + + onHide(item: any, prop: any) { + item[prop] = !item[prop]; + } + + addHeader() { + this.block.headers.push({ + tag: `Condition_${this.block.headers.length}`, + type: 'equal', + value: '', + actor: '', + }) + } + + onRemoveHeader(i: number) { + this.block.headers.splice(i, 1); + } + + getIcon(block: any) { + return this.registeredBlocks.getIcon(block.blockType); + } + + editBody($event: MouseEvent) { + const dialogRef = this.dialog.open(CodeEditorDialogComponent, { + width: '80%', + panelClass: 'g-dialog', + data: { + mode: 'json', + expression: this.block.messageBody, + readonly: this.readonly + }, + autoFocus: true, + disableClose: true + }) + dialogRef.afterClosed().subscribe(result => { + console.log(result); + this.block.messageBody = result.expression; + }) + } +} diff --git a/frontend/src/app/policy-engine/policy-configuration/blocks/report/report-item-config/report-item-config.component.html b/frontend/src/app/policy-engine/policy-configuration/blocks/report/report-item-config/report-item-config.component.html index 1f4b138ec4..cb08279cd2 100644 --- a/frontend/src/app/policy-engine/policy-configuration/blocks/report/report-item-config/report-item-config.component.html +++ b/frontend/src/app/policy-engine/policy-configuration/blocks/report/report-item-config/report-item-config.component.html @@ -1,35 +1,45 @@ - + + + + + + + - + - + - + - + - + - - @@ -83,15 +95,19 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - -
+ expand_more + Properties
Title
Description
Visible
Multiple - +
Icon Type @@ -40,27 +50,28 @@
Icon +
-
+
-
-
@@ -70,7 +81,8 @@
+ expand_more Dynamic Filters
+ expand_more Dynamic Filter {{i}} - - {{block.dynamicFilters[i].nextItemField}} {{block.dynamicFilters[i].type}} {{block.dynamicFilters[i].field}} + + {{block.dynamicFilters[i].nextItemField}} {{block.dynamicFilters[i].type}} + {{block.dynamicFilters[i].field}} delete @@ -122,11 +138,106 @@ Next Item Field Path - + +
+ expand_more + Common Variables
policyIdCurrent Policy ID
ownerDocument Owner
actionIdMint VC Document ID
actionSubjectIdMint Credential Subject ID
documentIdFirst Source VC Document id
documentSubjectIdFirst Source Credential Subject id
documentIdsSource VC Document ids
documentSubjectIdsSource Credential Subject ids
+ expand_more + Variables +
+ add + Add Variable +
+
+ + expand_more + + Variable {{i}} + + delete + +
Name + +
Value + + +
expand_more @@ -148,7 +259,8 @@ Filter {{i}} - + {{block.filters[i].field}} {{block.filters[i].type}} {{block.filters[i].value}} @@ -157,7 +269,7 @@
Type @@ -169,7 +281,7 @@
Variable Type @@ -179,14 +291,14 @@
Field
Value @@ -194,47 +306,4 @@
- expand_more - Variables -
- add - Add Variable -
-
- - expand_more - - Variable {{i}} - - delete - -
Name - -
Value - -
+ \ No newline at end of file diff --git a/frontend/src/app/policy-engine/policy-configuration/blocks/report/report-item-config/report-item-config.component.ts b/frontend/src/app/policy-engine/policy-configuration/blocks/report/report-item-config/report-item-config.component.ts index 6ae2c4185c..9cceee368e 100644 --- a/frontend/src/app/policy-engine/policy-configuration/blocks/report/report-item-config/report-item-config.component.ts +++ b/frontend/src/app/policy-engine/policy-configuration/blocks/report/report-item-config/report-item-config.component.ts @@ -27,9 +27,11 @@ export class ReportItemConfigComponent implements OnInit { propHidden: any = { main: false, + properties: false, filterGroup: false, filters: {}, variableGroup: false, + commonVariableGroup: true, variables: {}, dynamicFilterGroup: false, dynamicFilters: {} diff --git a/frontend/src/app/policy-engine/policy-configuration/blocks/tokens/mint-config/mint-config.component.html b/frontend/src/app/policy-engine/policy-configuration/blocks/tokens/mint-config/mint-config.component.html index 9ac5d5a896..7670a0dbaa 100644 --- a/frontend/src/app/policy-engine/policy-configuration/blocks/tokens/mint-config/mint-config.component.html +++ b/frontend/src/app/policy-engine/policy-configuration/blocks/tokens/mint-config/mint-config.component.html @@ -4,7 +4,7 @@ expand_more - UI + Properties - {{property.label}} + * {{property.label}} @@ -24,7 +24,7 @@ - {{property.label}} + * {{property.label}}
@@ -61,7 +61,9 @@ - {{property.label}} + + * {{property.label}} + @@ -71,7 +73,9 @@ - {{property.label}} + + * {{property.label}} + @@ -81,7 +85,9 @@ - {{property.label}} + + * {{property.label}} + {{item.label}} @@ -93,7 +99,9 @@ - {{property.label}} + + * {{property.label}} + @@ -102,7 +110,8 @@ ({{schema.status}}) - + {{value}} @@ -115,7 +124,8 @@
- + {{value}} @@ -138,7 +148,9 @@ - {{property.label}} + + * {{property.label}} + @@ -154,7 +166,9 @@ - {{property.label}} + + * {{property.label}} + diff --git a/frontend/src/app/policy-engine/policy-configuration/common-property/common-property.component.ts b/frontend/src/app/policy-engine/policy-configuration/common-property/common-property.component.ts index bc134b77e3..78528bda20 100644 --- a/frontend/src/app/policy-engine/policy-configuration/common-property/common-property.component.ts +++ b/frontend/src/app/policy-engine/policy-configuration/common-property/common-property.component.ts @@ -83,7 +83,9 @@ export class CommonPropertyComponent implements OnInit { this.allBlocks = []; } this.childrenBlocks = this.allBlocks.filter(item => item.parent === this.data?.id); - } else { + } + + if (this.property.type !== 'Group' && this.property.type !== 'Array') { if (this.property.default && !this.data.hasOwnProperty(this.property.name)) { this.data[this.property.name] = this.property.default; } diff --git a/frontend/src/app/policy-engine/policy-engine.module.ts b/frontend/src/app/policy-engine/policy-engine.module.ts index 95a7cc8136..e3c73721ca 100644 --- a/frontend/src/app/policy-engine/policy-engine.module.ts +++ b/frontend/src/app/policy-engine/policy-engine.module.ts @@ -76,6 +76,9 @@ import { CreateTokenConfigComponent } from './policy-configuration/blocks/tokens import { TokenConfigurationComponent } from '../components/token-configuration/token-configuration.component'; import { TreeNodeActionsComponent } from './helpers/tree-node-actions/tree-node-actions.component'; import { MultiPolicyDialogComponent } from './helpers/multi-policy-dialog/multi-policy-dialog.component'; +import { + HttpRequestConfigComponent +} from './policy-configuration/blocks/main/http-request-config/http-request-config.component'; @NgModule({ declarations: [ @@ -125,6 +128,7 @@ import { MultiPolicyDialogComponent } from './helpers/multi-policy-dialog/multi- CustomLogicConfigComponent, CodeEditorDialogComponent, SwitchConfigComponent, + HttpRequestConfigComponent, EventsOverview, ConfirmationDialog, RevokeConfigComponent, diff --git a/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.css b/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.css index 41841d20f9..4ab1aae444 100644 --- a/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.css +++ b/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.css @@ -62,7 +62,8 @@ a { cursor: pointer; text-decoration: underline; } -a[disabled="true"]{ + +a[disabled="true"] { color: rgb(150, 150, 150); cursor: default; pointer-events: none; @@ -72,6 +73,7 @@ a[disabled="true"]{ .table .mat-column-issuer { width: 510px; } + .table .mat-column-owner a, .table .mat-column-issuer a { max-width: 500px; @@ -91,7 +93,7 @@ a[disabled="true"]{ } .filter-label { - color: rgba(0,0,0,.7); + color: rgba(0, 0, 0, .7); padding: 0px 8px 0 3px; text-transform: capitalize; } @@ -114,14 +116,13 @@ a[disabled="true"]{ height: 25px; } -.display-chain { -} +.display-chain {} .container { display: flex; } -.container > mat-icon { +.container>mat-icon { font-size: 50px; flex: 0 0 80px; align-self: center; @@ -134,7 +135,7 @@ a[disabled="true"]{ user-select: none; cursor: pointer; height: 220px; - margin-top: 50px; + margin-top: 80px; } .scroll-right:hover { @@ -225,7 +226,7 @@ a[disabled="true"]{ transform: rotate(90deg); } -.chain-item:last-child .chain-arrow{ +.chain-item:last-child .chain-arrow { display: none; } @@ -363,7 +364,7 @@ a.open-vp { } .vp-signature { - margin: 20px 50px; + margin: 20px 50px 20px 4px; display: flex; align-items: center; gap: 20px; @@ -375,14 +376,16 @@ a.open-vp { padding: 3px 0; } -.parties-value, .nested-documents-value { +.parties-value, +.nested-documents-value { font-size: 20px; color: #0C77FF; font-weight: bold; line-height: 25px; } -.parties, .nested-documents { +.parties, +.nested-documents { display: flex; gap: 10px; overflow-y: auto; @@ -428,13 +431,12 @@ a.open-vp { display: inline-block; } -.multiple-documents-container .chain-item -{ +.multiple-documents-container .chain-item { transition: transform 1s ease; } .active-multiple-document { - transform: translate(0,0); + transform: translate(0, 0); z-index: 3; border: 2px solid gray; } @@ -465,7 +467,7 @@ a.open-vp { display: none; } -.multiple-documents-count { +.multiple-documents-count { position: absolute; right: 42px; bottom: 12px; @@ -505,3 +507,88 @@ a.open-vp { border-bottom: 2px solid gray; transition: transform 1s linear; } + + +.card-item { + margin: 6px 30px 6px 2px; + padding: 16px 24px; + position: relative; + display: inline-block; + box-sizing: border-box; + width: 370px; + height: 150px; + background: #FFFFFF 0% 0% no-repeat padding-box; + box-shadow: 0px 3px 6px #00000029; + border-radius: 26px; + vertical-align: bottom; +} + +.card-title { + font-size: 16px; + color: #707070; + font-weight: bold; + margin-bottom: 16px; + height: 20px; +} + +.card-item a.open-vc { + text-decoration: none; + font-size: 16px; + color: #0B73F8; + font-weight: bold; + display: inline-block; + cursor: pointer; +} + +.card-right { + position: absolute; + right: 24px; + top: 16px; +} + +.card-item[main="true"] { + border: 1px solid #00a100; +} + +.card-item[main="false"] { + border: 1px solid #b7b7b7; +} + +.card-item[main="true"] mat-icon { + color: #00a100; +} + +.card-item[main="true"] .card-icon { + display: inline-block; +} + +.card-item[main="false"] .card-icon { + display: none; +} + +.card-icon { + position: relative; + height: 14px; + width: 24px; + line-height: 14px; +} + +.card-icon mat-icon { + position: absolute; + top: -3px; + left: -3px; +} + + +.impact-section { + display: flex; +} + +*[offset="true"] { + border-left: 1px solid rgb(112 112 112 / 64%); + padding-left: 28px; +} + +*[vp-section-offset="true"] { + margin: 0 54px 30px 104px; +} \ No newline at end of file diff --git a/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.html b/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.html index 68d80e6b73..de3982ad54 100644 --- a/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.html +++ b/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.html @@ -5,7 +5,7 @@
-
+ HASH/ID: @@ -15,53 +15,147 @@
-
+
Can't find document with HASH: {{this.hash}}
-
-
- -
+
+
+ -
+ -
+
Token & Issuer - VC File + VC File
Token
-
{{mintDocument.tokenId}}
+
{{item.mintDocument.tokenId}}
Receipt Name
-
{{mintDocument.username}}
+
{{item.mintDocument.username}}
+
+
+
Token amount
+
{{item.mintDocument.amount}}
Mint date
-
{{mintDocument.date}}
+
{{item.mintDocument.date}}
-
+
+
+
+ Primary Impact +
+
+
+
+
+
+
+ workspace_premium +
+ {{item.label}} +
+
+ VC File +
+
+
+ {{item.description}} +
+
+ {{item.amount}} + {{item.unit}} +
+
+
+
+
+
+
+
+
+ Secondary Impacts +
+
+
+
+
+
+
+ workspace_premium +
+ {{item.label}} +
+
+ VC File +
+
+
+ {{item.description}} +
+
+ {{item.amount}} + {{item.unit}} +
+
+
+
+
+
+
+
+ +
+
+ This Carbon Offset Claim has met all the requirements as issued in the policy secured to this token. +
+
+
+
Verified Signature:
+
+ {{item.vpDocument.document.document?.proof?.jws}}
+
+
+
Verified Signature:
+
+ {{item.vcDocument.document.document?.proof?.jws}}
+
+
+
+
+ +
+
Policy Overview VC File @@ -86,19 +180,6 @@
- -
- This Carbon Offset Claim has met all the requirements as issued in the policy secured to this token. -
- -
-
Verified Signature:
-
{{vpDocument.document.document?.proof?.jws}}
-
-
-
Verified Signature:
-
{{vcDocument.document.document?.proof?.jws}}
-
@@ -114,7 +195,8 @@ 'opacity': document.index === 0 && item.dynamicFilters.length && item.document.length > 1 ? 1 : 0, 'transform': document.index === 0 && item.dynamicFilters.length && item.document.length > 1 ? 'scale(1, 1) translate(0, 0)' : 'scale(1, 0) translate(20px, 0)' }">
- account_tree
+
warning Revoked with reason: "{{document.document.comment}}"
@@ -147,7 +230,8 @@
- + {{link.title}} @@ -157,9 +241,11 @@
- chevron_left + chevron_left + {{item.activeDocumentIndex}} in {{item.document.length}} - chevron_right + chevron_right +
diff --git a/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.ts b/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.ts index 71057c37c9..920749662b 100644 --- a/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.ts +++ b/frontend/src/app/policy-engine/policy-viewer/blocks/report-block/report-block.component.ts @@ -3,13 +3,30 @@ import { FormBuilder, Validators } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { MatIconRegistry } from '@angular/material/icon'; import { DomSanitizer } from '@angular/platform-browser'; -import { IconType, IPolicyReport, IReport, IReportItem, ITokenReport, IVCReport, IVPReport } from '@guardian/interfaces'; +import { + IImpactReport, + IconType, + IPolicyReport, + IReport, + IReportItem, + ITokenReport, + IVCReport, + IVPReport +} from '@guardian/interfaces'; import { VCViewerDialog } from 'src/app/schema-engine/vc-dialog/vc-dialog.component'; import { IPFSService } from 'src/app/services/ipfs.service'; import { PolicyEngineService } from 'src/app/services/policy-engine.service'; import { WebSocketService } from 'src/app/services/web-socket.service'; import { IconsArray } from './iconsArray'; +interface IAdditionalDocument { + vpDocument?: IVPReport | undefined; + vcDocument?: IVCReport | undefined; + mintDocument?: ITokenReport | undefined; + primaryImpacts?: IImpactReport[] | undefined; + secondaryImpacts?: IImpactReport[] | undefined; +} + /** * Component for display block of 'ReportBlock' types. */ @@ -28,9 +45,7 @@ export class ReportBlockComponent implements OnInit { socket: any; content: string | null = null; chainVisible: boolean = false; - vpDocument: IVPReport | undefined; - vcDocument: IVCReport | undefined; - mintDocument: ITokenReport | undefined; + mainDocuments: IAdditionalDocument[] | undefined; policyDocument: IPolicyReport | undefined; documents: any; policyCreatorDocument: IReportItem | undefined; @@ -100,9 +115,7 @@ export class ReportBlockComponent implements OnInit { this.loadTrustChainData(data); } else { this.chainVisible = false; - this.vpDocument = undefined; - this.vcDocument = undefined; - this.mintDocument = undefined; + this.mainDocuments = undefined; this.policyDocument = undefined; this.documents = undefined; this.hash = ""; @@ -116,12 +129,25 @@ export class ReportBlockComponent implements OnInit { this.searchForm.patchValue({ value: this.hash }); - this.vpDocument = report.vpDocument; - this.vcDocument = report.vcDocument; - this.mintDocument = report.mintDocument; this.policyDocument = report.policyDocument; this.policyCreatorDocument = report.policyCreatorDocument; this.documents = report.documents || []; + + const mainDocument = this.createAdditionalDocument(report); + if (mainDocument) { + this.mainDocuments = [mainDocument]; + if (report.additionalDocuments) { + for (const doc of report.additionalDocuments) { + const additionalDocument = this.createAdditionalDocument(doc); + if (additionalDocument) { + this.mainDocuments.push(additionalDocument); + } + } + } + } else { + this.mainDocuments = undefined; + } + if (this.policyDocument) { this.documents.push({ type: this.policyDocument.type, @@ -137,7 +163,7 @@ export class ReportBlockComponent implements OnInit { if (this.policyCreatorDocument) { this.documents.push(this.policyCreatorDocument); } - this.documents = this.documents.filter((e: any)=>e.visible); + this.documents = this.documents.filter((e: any) => e.visible); this.documents = this.documents.reverse(); this.documents.forEach((reportItem: any) => { if (!Array.isArray(reportItem.document) && reportItem.document) { @@ -162,8 +188,21 @@ export class ReportBlockComponent implements OnInit { this.loading = false; } + createAdditionalDocument(report: any): IAdditionalDocument | null { + if (!(report.vpDocument || report.vcDocument)) { + return null; + } + const result: IAdditionalDocument = {}; + result.vpDocument = report.vpDocument; + result.vcDocument = report.vcDocument; + result.mintDocument = report.mintDocument; + const impacts: any[] = report.impacts; + result.primaryImpacts = impacts?.filter(i => i.impactType === 'Primary Impacts'); + result.secondaryImpacts = impacts?.filter(i => i.impactType !== 'Primary Impacts'); + return result; + } - openVCDocument(item: IVCReport | ITokenReport | IPolicyReport | IReportItem, document?: any) { + openVCDocument(item: IVCReport | ITokenReport | IPolicyReport | IReportItem | IImpactReport, document?: any) { const dialogRef = this.dialog.open(VCViewerDialog, { width: '850px', data: { diff --git a/frontend/src/app/policy-engine/policy-viewer/blocks/step-block/step-block.component.ts b/frontend/src/app/policy-engine/policy-viewer/blocks/step-block/step-block.component.ts index e855b7467f..439fc966a9 100644 --- a/frontend/src/app/policy-engine/policy-viewer/blocks/step-block/step-block.component.ts +++ b/frontend/src/app/policy-engine/policy-viewer/blocks/step-block/step-block.component.ts @@ -8,81 +8,81 @@ import { WebSocketService } from 'src/app/services/web-socket.service'; * Component for display block of 'interfaceStepBlock' types. */ @Component({ - selector: 'step-block', - templateUrl: './step-block.component.html', - styleUrls: ['./step-block.component.css'] + selector: 'step-block', + templateUrl: './step-block.component.html', + styleUrls: ['./step-block.component.css'] }) export class StepBlockComponent implements OnInit { - private socket: Subscription | null; + private socket: Subscription | null; - get loading(): boolean { - return !this.blocks || !this.blocks.length || !this.activeBlock; - } + get loading(): boolean { + return !this.blocks || !this.blocks.length || !this.activeBlock; + } - get activeBlock(): any { - return this.blocks && this.blocks[this.index] || (this.index === -1); - } + get activeBlock(): any { + return this.blocks && this.blocks[this.index] || (this.index === -1); + } - @Input('id') id!: string; - @Input('policyId') policyId!: string; - @Input('static') static!: any; + @Input('id') id!: string; + @Input('policyId') policyId!: string; + @Input('static') static!: any; - blocks: any; - activeBlockId: any; - isActive = false; - private index: number = 0; + blocks: any; + activeBlockId: any; + isActive = false; + private index: number = 0; - constructor( - private policyEngineService: PolicyEngineService, - private wsService: WebSocketService, - private policyHelper: PolicyHelper, - ) { - this.socket = null; - } + constructor( + private policyEngineService: PolicyEngineService, + private wsService: WebSocketService, + private policyHelper: PolicyHelper, + ) { + this.socket = null; + } - ngOnInit(): void { - if (!this.static) { - this.socket = this.wsService.blockSubscribe(this.onUpdate.bind(this)); + ngOnInit(): void { + if (!this.static) { + this.socket = this.wsService.blockSubscribe(this.onUpdate.bind(this)); + } + this.loadData(); } - this.loadData(); - } - ngOnDestroy(): void { - if (this.socket) { - this.socket.unsubscribe(); + ngOnDestroy(): void { + if (this.socket) { + this.socket.unsubscribe(); + } } - } - onUpdate(id: string): void { - if (this.id == id) { - this.loadData(); + onUpdate(id: string): void { + if (this.id == id) { + this.loadData(); + } } - } - loadData() { - if (this.static) { - this.setData(this.static); - setTimeout(() => { - }, 500); - } else { - this.policyEngineService.getBlockData(this.id, this.policyId).subscribe((data: any) => { - this.setData(data); - }, (e) => { - console.error(e.error); - }); + loadData() { + if (this.static) { + this.setData(this.static); + setTimeout(() => { + }, 500); + } else { + this.policyEngineService.getBlockData(this.id, this.policyId).subscribe((data: any) => { + this.setData(data); + }, (e) => { + console.error(e.error); + }); + } } - } - setData(data: any) { - if (data) { - this.isActive = true; - this.blocks = data.blocks || []; - this.index = data.index; - } else { - this.blocks = null; - this.index = 0; - this.isActive = false; + setData(data: any) { + if (data) { + this.isActive = true; + this.blocks = data.blocks || []; + this.index = data.index; + } else { + this.blocks = null; + this.index = 0; + this.isActive = false; + } } - } } diff --git a/frontend/src/app/policy-engine/policy-viewer/blocks/token-confirmation-block/token-confirmation-block.component.html b/frontend/src/app/policy-engine/policy-viewer/blocks/token-confirmation-block/token-confirmation-block.component.html index fd80345582..56e250ed74 100644 --- a/frontend/src/app/policy-engine/policy-viewer/blocks/token-confirmation-block/token-confirmation-block.component.html +++ b/frontend/src/app/policy-engine/policy-viewer/blocks/token-confirmation-block/token-confirmation-block.component.html @@ -6,9 +6,9 @@
Token {{action}}
Please enter the private key for the account - ({{accountId}}) + ({{accountId}}) to {{action}} it with the token {{tokenName}} - ({{tokenId}}) + ({{tokenId}})
@@ -29,4 +29,4 @@
-
\ No newline at end of file +
diff --git a/frontend/src/app/policy-engine/registered-blocks.ts b/frontend/src/app/policy-engine/registered-blocks.ts index a936ef9838..039ae243d4 100644 --- a/frontend/src/app/policy-engine/registered-blocks.ts +++ b/frontend/src/app/policy-engine/registered-blocks.ts @@ -1,56 +1,103 @@ -import { Injectable } from "@angular/core"; -import { ActionConfigComponent } from "./policy-configuration/blocks/main/action-config/action-config.component"; -import { AggregateConfigComponent } from "./policy-configuration/blocks/documents/aggregate-config/aggregate-config.component"; -import { ContainerConfigComponent } from "./policy-configuration/blocks/main/container-config/container-config.component"; -import { DocumentSourceComponent } from "./policy-configuration/blocks/documents/document-viewer-config/document-viewer-config.component"; -import { ExternalDataConfigComponent } from "./policy-configuration/blocks/documents/external-data-config/external-data-config.component"; -import { FiltersAddonConfigComponent } from "./policy-configuration/blocks/documents/filters-addon-config/filters-addon-config.component"; -import { InformationConfigComponent } from "./policy-configuration/blocks/main/information-config/information-config.component"; -import { MintConfigComponent } from "./policy-configuration/blocks/tokens/mint-config/mint-config.component"; -import { RequestConfigComponent } from "./policy-configuration/blocks/documents/request-config/request-config.component"; -import { RolesConfigComponent } from "./policy-configuration/blocks/main/roles-config/roles-config.component"; -import { SendConfigComponent } from "./policy-configuration/blocks/documents/send-config/send-config.component"; -import { SourceAddonConfigComponent } from "./policy-configuration/blocks/documents/source-addon-config/source-addon-config.component"; -import { ActionBlockComponent } from "./policy-viewer/blocks/action-block/action-block.component"; -import { ContainerBlockComponent } from "./policy-viewer/blocks/container-block/container-block.component"; -import { DocumentsSourceBlockComponent } from "./policy-viewer/blocks/documents-source-block/documents-source-block.component"; -import { FiltersAddonBlockComponent } from "./policy-viewer/blocks/filters-addon-block/filters-addon-block.component"; -import { InformationBlockComponent } from "./policy-viewer/blocks/information-block/information-block.component"; -import { RequestDocumentBlockComponent } from "./policy-viewer/blocks/request-document-block/request-document-block.component"; -import { RolesBlockComponent } from "./policy-viewer/blocks/roles-block/roles-block.component"; -import { StepBlockComponent } from "./policy-viewer/blocks/step-block/step-block.component"; -import { CalculateConfigComponent } from './policy-configuration/blocks/calculate/calculate-config/calculate-config.component'; -import { CalculateMathConfigComponent } from './policy-configuration/blocks/calculate/calculate-math-config/calculate-math-config.component'; -import { BlockNode } from "./helpers/tree-data-source/tree-data-source"; -import { ReportBlockComponent } from "./policy-viewer/blocks/report-block/report-block.component"; -import { ReportItemConfigComponent } from "./policy-configuration/blocks/report/report-item-config/report-item-config.component"; -import { PaginationAddonBlockComponent } from './policy-viewer/blocks/pagination-addon-block/pagination-addon-block.component'; -import { ReassigningConfigComponent } from "./policy-configuration/blocks/documents/reassigning-config/reassigning-config.component"; -import { TimerConfigComponent } from "./policy-configuration/blocks/documents/timer-config/timer-config.component"; -import { CustomLogicConfigComponent } from './policy-configuration/blocks/calculate/custom-logic-config/custom-logic-config.component'; -import { SwitchConfigComponent } from "./policy-configuration/blocks/main/switch-config/switch-config.component"; -import { PolicyBlockModel } from "./structures/policy-model"; -import { RevokeConfigComponent } from "./policy-configuration/blocks/documents/revoke-config/revoke-config.component"; -import { ButtonConfigComponent } from "./policy-configuration/blocks/main/button-config/button-config.component"; -import { ButtonBlockComponent } from "./policy-viewer/blocks/button-block/button-block.component"; +import { Injectable } from '@angular/core'; +import { ActionConfigComponent } from './policy-configuration/blocks/main/action-config/action-config.component'; +import { + AggregateConfigComponent +} from './policy-configuration/blocks/documents/aggregate-config/aggregate-config.component'; +import { + ContainerConfigComponent +} from './policy-configuration/blocks/main/container-config/container-config.component'; +import { + DocumentSourceComponent +} from './policy-configuration/blocks/documents/document-viewer-config/document-viewer-config.component'; +import { + ExternalDataConfigComponent +} from './policy-configuration/blocks/documents/external-data-config/external-data-config.component'; +import { + FiltersAddonConfigComponent +} from './policy-configuration/blocks/documents/filters-addon-config/filters-addon-config.component'; +import { + InformationConfigComponent +} from './policy-configuration/blocks/main/information-config/information-config.component'; +import { MintConfigComponent } from './policy-configuration/blocks/tokens/mint-config/mint-config.component'; +import { + RequestConfigComponent +} from './policy-configuration/blocks/documents/request-config/request-config.component'; +import { RolesConfigComponent } from './policy-configuration/blocks/main/roles-config/roles-config.component'; +import { SendConfigComponent } from './policy-configuration/blocks/documents/send-config/send-config.component'; +import { + SourceAddonConfigComponent +} from './policy-configuration/blocks/documents/source-addon-config/source-addon-config.component'; +import { ActionBlockComponent } from './policy-viewer/blocks/action-block/action-block.component'; +import { ContainerBlockComponent } from './policy-viewer/blocks/container-block/container-block.component'; +import { + DocumentsSourceBlockComponent +} from './policy-viewer/blocks/documents-source-block/documents-source-block.component'; +import { FiltersAddonBlockComponent } from './policy-viewer/blocks/filters-addon-block/filters-addon-block.component'; +import { InformationBlockComponent } from './policy-viewer/blocks/information-block/information-block.component'; +import { + RequestDocumentBlockComponent +} from './policy-viewer/blocks/request-document-block/request-document-block.component'; +import { RolesBlockComponent } from './policy-viewer/blocks/roles-block/roles-block.component'; +import { StepBlockComponent } from './policy-viewer/blocks/step-block/step-block.component'; +import { + CalculateConfigComponent +} from './policy-configuration/blocks/calculate/calculate-config/calculate-config.component'; +import { + CalculateMathConfigComponent +} from './policy-configuration/blocks/calculate/calculate-math-config/calculate-math-config.component'; +import { BlockNode } from './helpers/tree-data-source/tree-data-source'; +import { ReportBlockComponent } from './policy-viewer/blocks/report-block/report-block.component'; +import { + ReportItemConfigComponent +} from './policy-configuration/blocks/report/report-item-config/report-item-config.component'; +import { + PaginationAddonBlockComponent +} from './policy-viewer/blocks/pagination-addon-block/pagination-addon-block.component'; +import { + ReassigningConfigComponent +} from './policy-configuration/blocks/documents/reassigning-config/reassigning-config.component'; +import { TimerConfigComponent } from './policy-configuration/blocks/documents/timer-config/timer-config.component'; +import { + CustomLogicConfigComponent +} from './policy-configuration/blocks/calculate/custom-logic-config/custom-logic-config.component'; +import { SwitchConfigComponent } from './policy-configuration/blocks/main/switch-config/switch-config.component'; +import { PolicyBlockModel } from './structures/policy-model'; +import { RevokeConfigComponent } from './policy-configuration/blocks/documents/revoke-config/revoke-config.component'; +import { ButtonConfigComponent } from './policy-configuration/blocks/main/button-config/button-config.component'; +import { ButtonBlockComponent } from './policy-viewer/blocks/button-block/button-block.component'; import { GenerateUUIDv4 } from '@guardian/interfaces'; -import { TokenActionConfigComponent } from "./policy-configuration/blocks/tokens/token-action-config/token-action-config.component"; -import { DocumentValidatorConfigComponent } from "./policy-configuration/blocks/documents/document-validator-config/document-validator-config.component"; -import { TokenConfirmationConfigComponent } from "./policy-configuration/blocks/tokens/token-confirmation-config/token-confirmation-config.component"; -import { TokenConfirmationBlockComponent } from "./policy-viewer/blocks/token-confirmation-block/token-confirmation-block.component"; -import { GroupManagerConfigComponent } from './policy-configuration/blocks/main/group-manager-config/group-manager-config.component'; +import { + TokenActionConfigComponent +} from './policy-configuration/blocks/tokens/token-action-config/token-action-config.component'; +import { + DocumentValidatorConfigComponent +} from './policy-configuration/blocks/documents/document-validator-config/document-validator-config.component'; +import { + TokenConfirmationConfigComponent +} from './policy-configuration/blocks/tokens/token-confirmation-config/token-confirmation-config.component'; +import { + TokenConfirmationBlockComponent +} from './policy-viewer/blocks/token-confirmation-block/token-confirmation-block.component'; +import { + GroupManagerConfigComponent +} from './policy-configuration/blocks/main/group-manager-config/group-manager-config.component'; import { GroupManagerBlockComponent } from './policy-viewer/blocks/group-manager-block/group-manager-block.component'; -import { BlockType } from "./structures/types/block-type.type"; -import { BlockGroup } from "./structures/types/block-group.type"; -import { BlockHeaders } from "./structures/types/block-headers.type"; -import { IBlockAbout } from "./structures/interfaces/block-about.interface"; -import { ChildrenType } from "./structures/types/children-type.type"; -import { ControlType } from "./structures/types/control-type.type"; -import { BlockAbout } from "./structures/block-about"; -import { IBlockSetting } from "./structures/interfaces/block-setting.interface"; -import { MultiSignBlockComponent } from "./policy-viewer/blocks/multi-sign-block/multi-sign-block.component"; -import { CreateTokenConfigComponent } from "./policy-configuration/blocks/tokens/create-token-config/create-token-config.component"; -import { CreateTokenBlockComponent } from "./policy-viewer/blocks/create-token-block/create-token-block.component"; +import { BlockType } from './structures/types/block-type.type'; +import { BlockGroup } from './structures/types/block-group.type'; +import { BlockHeaders } from './structures/types/block-headers.type'; +import { IBlockAbout } from './structures/interfaces/block-about.interface'; +import { ChildrenType } from './structures/types/children-type.type'; +import { ControlType } from './structures/types/control-type.type'; +import { BlockAbout } from './structures/block-about'; +import { IBlockSetting } from './structures/interfaces/block-setting.interface'; +import { MultiSignBlockComponent } from './policy-viewer/blocks/multi-sign-block/multi-sign-block.component'; +import { + CreateTokenConfigComponent +} from './policy-configuration/blocks/tokens/create-token-config/create-token-config.component'; +import { CreateTokenBlockComponent } from './policy-viewer/blocks/create-token-block/create-token-block.component'; +import { + HttpRequestConfigComponent +} from './policy-configuration/blocks/main/http-request-config/http-request-config.component'; @Injectable() export class RegisteredBlocks { @@ -97,6 +144,7 @@ export class RegisteredBlocks { { type: BlockType.Container }, { type: BlockType.Step }, { type: BlockType.Switch }, + { type: BlockType.HttpRequest }, { type: BlockType.DocumentsViewer }, { type: BlockType.Request }, { type: BlockType.SendToGuardian }, @@ -117,7 +165,7 @@ export class RegisteredBlocks { { type: BlockType.DocumentValidatorBlock }, { type: BlockType.MultiSignBlock }, { type: BlockType.CreateToken }, - { type: BlockType.SplitBlock } + { type: BlockType.SplitBlock }, ]; // #region Main, UI Components @@ -243,6 +291,17 @@ export class RegisteredBlocks { }); // #endregion + // #region Main, Server Blocks + this.registerBlock({ + type: BlockType.HttpRequest, + icon: 'http', + group: BlockGroup.Main, + header: BlockHeaders.ServerBlocks, + factory: null, + property: HttpRequestConfigComponent, + }); + // #endregion + // #region Documents, UI Components this.registerBlock({ type: BlockType.DocumentsViewer, @@ -421,6 +480,10 @@ export class RegisteredBlocks { header: BlockHeaders.ServerBlocks, factory: null, property: MintConfigComponent, + allowedChildren: [{ + type: BlockType.ImpactAddon, + group: BlockGroup.UnGrouped, + }] }); this.registerBlock({ type: BlockType.Wipe, @@ -448,6 +511,17 @@ export class RegisteredBlocks { }); // #endregion + // #region Calculate, Addons + this.registerBlock({ + type: BlockType.ImpactAddon, + icon: 'receipt', + group: BlockGroup.Tokens, + header: BlockHeaders.Addons, + factory: null, + property: null, + }); + // #endregion + // #region Calculate, Server Blocks this.registerBlock({ type: BlockType.Calculate, @@ -459,7 +533,7 @@ export class RegisteredBlocks { allowedChildren: [{ type: BlockType.CalculateMathAddon, group: BlockGroup.UnGrouped, - },{ + }, { type: BlockType.CalculateMathVariables, group: BlockGroup.UnGrouped, }] diff --git a/frontend/src/app/policy-engine/structures/types/block-type.type.ts b/frontend/src/app/policy-engine/structures/types/block-type.type.ts index 0e20bd51ca..6c48fb8e75 100644 --- a/frontend/src/app/policy-engine/structures/types/block-type.type.ts +++ b/frontend/src/app/policy-engine/structures/types/block-type.type.ts @@ -33,5 +33,7 @@ export enum BlockType { MultiSignBlock = 'multiSignBlock', CalculateMathVariables = 'calculateMathVariables', CreateToken = 'createTokenBlock', - SplitBlock = 'splitBlock' + SplitBlock = 'splitBlock', + ImpactAddon = 'impactAddon', + HttpRequest = 'httpRequestBlock' } diff --git a/frontend/src/app/services/contract.service.ts b/frontend/src/app/services/contract.service.ts new file mode 100644 index 0000000000..043719325e --- /dev/null +++ b/frontend/src/app/services/contract.service.ts @@ -0,0 +1,142 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { API_BASE_URL } from './api'; + +/** + * Services for working with contracts. + */ +@Injectable() +export class ContractService { + private readonly url: string = `${API_BASE_URL}/contracts`; + + constructor(private http: HttpClient) {} + + public all(): Observable { + return this.http.get(`${this.url}/`); + } + + public page( + pageIndex?: number, + pageSize?: number + ): Observable> { + if (Number.isInteger(pageIndex) && Number.isInteger(pageSize)) { + return this.http.get( + `${this.url}?pageIndex=${pageIndex}&pageSize=${pageSize}`, + { observe: 'response' } + ); + } + return this.http.get(`${this.url}`, { observe: 'response' }); + } + + public create(description: string): Observable> { + return this.http.post(`${this.url}`, { + description, + }); + } + + public import( + contractId: string, + description: string + ): Observable> { + return this.http.post(`${this.url}/import`, { + contractId, + description, + }); + } + + public addUser( + userId: string, + contractId: string + ): Observable> { + return this.http.post(`${this.url}/${contractId}/user`, { + userId, + }); + } + + public updateStatus(contractId: string): Observable { + return this.http.post(`${this.url}/${contractId}/status`, null); + } + + public createPair( + contractId: string, + baseTokenId: string, + oppositeTokenId: string, + baseTokenCount: number, + oppositeTokenCount: number + ) { + return this.http.post(`${this.url}/${contractId}/pair`, { + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount, + }); + } + + public getPair(baseTokenId: string, oppositeTokenId: string) { + return this.http.get(`${this.url}/pair`, { + params: { + baseTokenId, + oppositeTokenId, + }, + }); + } + + public createRetireRequest( + contractId: string, + baseTokenId: string, + oppositeTokenId: string, + baseTokenCount: string, + oppositeTokenCount: string, + baseTokenSerials: number[], + oppositeTokenSerials: number[] + ) { + return this.http.post(`${this.url}/${contractId}/retire/request`, { + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount, + baseTokenSerials, + oppositeTokenSerials, + }); + } + + public getRetireRequestsAll(): Observable { + return this.http.get(`${this.url}/retire/request`); + } + + public getRetireRequestsPage( + contractId?: string, + pageIndex?: number, + pageSize?: number + ): Observable> { + if (Number.isInteger(pageIndex) && Number.isInteger(pageSize)) { + return this.http.get( + contractId + ? `${this.url}/retire/request?pageIndex=${pageIndex}&pageSize=${pageSize}&contractId=${contractId}` + : `${this.url}/retire/request?pageIndex=${pageIndex}&pageSize=${pageSize}`, + { observe: 'response' } + ); + } + return this.http.get( + contractId + ? `${this.url}/retire/request?contractId=${contractId}` + : `${this.url}/retire/request`, + { observe: 'response' } + ); + } + + public cancelContractRequest(requestId: string) { + return this.http.delete(`${this.url}/retire/request`, { + params: { + requestId, + }, + }); + } + + public retireTokens(requestId: string) { + return this.http.post(`${this.url}/retire`, { + requestId, + }); + } +} \ No newline at end of file diff --git a/frontend/src/app/views/contract-config/contract-config.component.css b/frontend/src/app/views/contract-config/contract-config.component.css new file mode 100644 index 0000000000..d7bbe86122 --- /dev/null +++ b/frontend/src/app/views/contract-config/contract-config.component.css @@ -0,0 +1,152 @@ +.content { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + display: block; +} + +.loading { + background: #fff; + position: fixed; + z-index: 99; + top: var(--header-height); + left: 0; + bottom: 0; + right: 0; + display: flex; + align-items: center; + justify-items: center; + justify-content: center; + align-content: center; +} + +.table { + width: 100%; +} + +.btn-publish::ng-deep > * { + background: #4caf50 !important; + color: #fff !important; + min-width: 121px; + cursor: pointer !important; +} + +.not-exist { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + font-size: 20px; + color: darkgrey; +} + +.table-container { + max-height: calc(100vh - var(--header-height) - 170px); + min-height: 100px; + overflow-y: auto; + position: relative; + z-index: 0; + border-top: 1px solid #eee; +} + +.table-container .mat-column-contractId { + padding-left: 8px; + width: 150px; + max-width: 150px; +} + +.table-container .mat-column-description { + border-right: 1px solid #dddddd; + padding-left: 8px; + max-width: 500px; + overflow: hidden; + text-overflow: ellipsis; +} + +.table-container .mat-column-operations { + padding-left: 8px; + width: 200px; + min-width: 200px; + max-width: 200px; + border-right: 1px solid #dddddd; +} + +.table-container .mat-column-retire { + padding-left: 8px; + width: 137px; + min-width: 137px; + max-width: 137px; +} + +.table-container .mat-column-owner { + width: 300px; + max-width: 500px; + overflow: hidden; + text-overflow: ellipsis; +} + +.actions-container { + display: block; + z-index: 1; +} + +.table-paginator { + height: 60px; + border-top: 1px solid #eee; + margin-top: 5px; +} + +.action-btn { + background: #4caf50; + color: #fff; + border-radius: 6px; + display: inline-block; + padding: 6px 0; + width: 173px; + margin: 0px 5px; + cursor: pointer; + font-weight: 500; + text-align: center; + -webkit-user-select: none; + user-select: none; + position: relative; + white-space: nowrap; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; +} + +.action-btn.view-btn { + width: 143px; +} + +.refresh-action-container { + display: flex; + justify-content: center; + align-items: center; + gap: 5px; +} + +.refresh-action-btn { + color: #3f51b5; + background-color: none; +} + +.spin { + animation-name: spin; + animation-duration: 3000ms; + animation-iteration-count: infinite; + animation-timing-function: linear; +} + +@keyframes spin { + from { + transform:rotate(360deg); + } + to { + transform:rotate(0deg); + } +} \ No newline at end of file diff --git a/frontend/src/app/views/contract-config/contract-config.component.html b/frontend/src/app/views/contract-config/contract-config.component.html new file mode 100644 index 0000000000..2b234acbe2 --- /dev/null +++ b/frontend/src/app/views/contract-config/contract-config.component.html @@ -0,0 +1,78 @@ +
+
+ +
+ + +
+ Before starting work you need to get DID here +
+
+ + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + +
Hedera Contract Id + {{element.contractId}} + Description + {{ element.description }} + Operations +
+ Wait to be added + +
+
+
+ Add Pair + swap_horizontal_circle +
+
+
+ + Operations + +
+
Retirement Requests +
+ View + visibility +
+
+
+
+ + +
+
+
diff --git a/frontend/src/app/views/contract-config/contract-config.component.ts b/frontend/src/app/views/contract-config/contract-config.component.ts new file mode 100644 index 0000000000..21f2c43c76 --- /dev/null +++ b/frontend/src/app/views/contract-config/contract-config.component.ts @@ -0,0 +1,276 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { IUser, Token } from '@guardian/interfaces'; +import { ProfileService } from 'src/app/services/profile.service'; +import { TokenService } from 'src/app/services/token.service'; +import { ContractService } from 'src/app/services/contract.service'; +import { AddPairDialogComponent } from 'src/app/components/add-pair-dialog/add-pair-dialog.component'; +import { MatIcon } from '@angular/material/icon'; +import { DataInputDialogComponent } from 'src/app/components/data-input-dialog/data-input-dialog.component'; + +/** + * Component for operating with Contracts + */ +@Component({ + selector: 'contract-config', + templateUrl: './contract-config.component.html', + styleUrls: ['./contract-config.component.css'], +}) +export class ContractConfigComponent implements OnInit, OnDestroy { + contracts: any[] | null; + columns: string[] = []; + role!: any; + loading: boolean = true; + isConfirmed: boolean = false; + pageIndex: number; + pageSize: number; + contractsCount: any = 0; + + operationsOptions = [ + { + id: 'addPair', + title: 'Add Pair', + description: 'Add Contract Pair.', + color: '#4caf50', + }, + { + id: 'addUser', + title: 'Add User', + description: 'Add User To Contract.', + color: '#9c27b0', + }, + ]; + + constructor( + private profileService: ProfileService, + private contractsService: ContractService, + private tokenService: TokenService, + private dialog: MatDialog + ) { + this.contracts = null; + this.pageIndex = 0; + this.pageSize = 100; + this.columns = ['contractId', 'description', 'operations', 'retire']; + } + + ngOnInit() { + this.loading = true; + this.loadContracts(); + } + + ngOnDestroy() {} + + loadContracts() { + this.contracts = null; + this.isConfirmed = false; + this.loading = true; + this.profileService.getProfile().subscribe( + (profile: IUser | null) => { + this.isConfirmed = !!(profile && profile.confirmed); + this.role = profile ? profile.role : null; + if (this.isConfirmed) { + this.loadAllContracts(); + } else { + setTimeout(() => { + this.loading = false; + }, 500); + } + }, + (e) => { + this.loading = false; + } + ); + } + + loadAllContracts() { + this.loading = true; + this.contractsService.page(this.pageIndex, this.pageSize).subscribe( + (policiesResponse) => { + this.contracts = policiesResponse.body || []; + this.contractsCount = + policiesResponse.headers.get('X-Total-Count') || + this.contracts.length; + setTimeout(() => { + this.loading = false; + }, 500); + }, + (e) => { + this.loading = false; + } + ); + } + + onPage(event: any) { + if (this.pageSize != event.pageSize) { + this.pageIndex = 0; + this.pageSize = event.pageSize; + } else { + this.pageIndex = event.pageIndex; + this.pageSize = event.pageSize; + } + this.loadAllContracts(); + } + + importContract() { + const dialogRef = this.dialog.open(DataInputDialogComponent, { + width: '500px', + autoFocus: false, + data: { + fieldsConfig: [ + { + name: 'contractId', + label: 'Contract Identifier', + placeholder: 'Contract Identifier', + required: true, + }, + { + name: 'description', + label: 'Description', + placeholder: 'Description', + required: false, + }, + ], + title: 'Import Contract', + }, + }); + dialogRef.afterClosed().subscribe(async (result) => { + if (result) { + this.loading = true; + this.contractsService + .import( + result.contractId?.trim(), + result.description?.trim() + ) + .subscribe( + () => { + this.loading = false; + this.loadContracts(); + }, + () => (this.loading = false) + ); + } + }); + } + + createContract() { + const dialogRef = this.dialog.open(DataInputDialogComponent, { + width: '500px', + autoFocus: false, + data: { + fieldsConfig: [ + { + name: 'description', + label: 'Description', + placeholder: 'Description', + required: false, + }, + ], + title: 'Create Contract', + }, + }); + dialogRef.afterClosed().subscribe(async (result) => { + if (!result) { + return; + } + this.loading = true; + this.contractsService.create(result.description?.trim()).subscribe( + (res) => { + this.loading = false; + this.loadContracts(); + }, + () => (this.loading = false) + ); + }); + } + + addUser(contractId: string) { + const dialogRef = this.dialog.open(DataInputDialogComponent, { + width: '500px', + autoFocus: false, + data: { + fieldsConfig: [ + { + name: 'userId', + label: 'User Identifier', + placeholder: 'User Identifier', + required: true, + }, + ], + title: 'Enter User Identifier', + }, + }); + dialogRef.afterClosed().subscribe(async (result) => { + if (!result) { + return; + } + this.loading = true; + this.contractsService.addUser(result.userId?.trim(), contractId).subscribe( + (res) => { + this.loading = false; + this.loadContracts(); + }, + () => (this.loading = false) + ); + }); + } + + onOperationAction(event: any, element: any) { + if (event.id === 'addUser') { + this.addUser(element.contractId); + } else if (event.id === 'addPair') { + this.addPair(element.contractId); + } + } + + addPair(contractId: string) { + this.loading = true; + this.tokenService.getTokens().subscribe( + (data: any) => { + this.loading = false; + const tokens = data + .map((e: any) => new Token(e)) + .filter((token: Token) => token.enableWipe); + const dialogRef = this.dialog.open(AddPairDialogComponent, { + width: '650px', + panelClass: 'g-dialog', + disableClose: true, + autoFocus: false, + data: { + tokens, + contractId, + }, + }); + dialogRef.afterClosed().subscribe(async (result) => { + if (result) { + this.loading = true; + this.contractsService.createPair( + result.contractId, + result.baseTokenId, + result.oppositeTokenId, + result.baseTokenCount, + result.oppositeTokenCount, + ).subscribe( + () => (this.loading = false), + () => (this.loading = false) + ); + } + }); + }, + (e) => { + this.loading = false; + console.error(e.error); + } + ); + } + + checkStatus(contractId: string, element: MatIcon) { + element._elementRef.nativeElement.classList.add('spin'); + this.contractsService.updateStatus(contractId).subscribe( + () => { + element._elementRef.nativeElement.classList.remove('spin'); + this.loadContracts(); + }, + () => element._elementRef.nativeElement.classList.remove('spin') + ); + } +} \ No newline at end of file diff --git a/frontend/src/app/views/contract-request-config/contract-request-config.component.css b/frontend/src/app/views/contract-request-config/contract-request-config.component.css new file mode 100644 index 0000000000..5f06177e94 --- /dev/null +++ b/frontend/src/app/views/contract-request-config/contract-request-config.component.css @@ -0,0 +1,98 @@ +.content { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + display: block; +} + +.loading { + background: #fff; + position: fixed; + z-index: 99; + top: var(--header-height); + left: 0; + bottom: 0; + right: 0; + display: flex; + align-items: center; + justify-items: center; + justify-content: center; + align-content: center; +} + +.table { + width: 100%; +} + +.not-exist { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + font-size: 20px; + color: darkgrey; +} + +.table-container { + max-height: calc(100vh - var(--header-height) - 170px); + min-height: 100px; + overflow-y: auto; + position: relative; + z-index: 0; + border-top: 1px solid #eee; +} + +.table-container .mat-column-retire { + padding-left: 8px; + width: 172px; + max-width: 172px; + min-width: 172px; +} + +.table-container .mat-column-oppositeTokenCount { + border-right: 1px solid #dddddd; +} + +.actions-container { + display: block; + z-index: 1; + padding: 20px 0px 10px 20px; +} + +.table-paginator { + height: 60px; + border-top: 1px solid #eee; + margin-top: 5px; +} + +.retire-action-btn, .view-action-btn { + color: #fff; + border-radius: 6px; + display: inline-block; + padding: 6px 0; + width: 180px; + margin: 0px 5px; + cursor: pointer; + font-weight: 500; + text-align: center; + -webkit-user-select: none; + user-select: none; + position: relative; + white-space: nowrap; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; +} +.view-action-btn { + background: #4caf50; +} +.retire-action-btn { + background: burlywood; +} + +.actions-container-contract { + width: 300px; +} \ No newline at end of file diff --git a/frontend/src/app/views/contract-request-config/contract-request-config.component.html b/frontend/src/app/views/contract-request-config/contract-request-config.component.html new file mode 100644 index 0000000000..c26e7ca687 --- /dev/null +++ b/frontend/src/app/views/contract-request-config/contract-request-config.component.html @@ -0,0 +1,80 @@ +
+
+ +
+ + +
+ Before starting work you need to get DID here +
+
+ + +
+ + Contract + + All Contracts + + {{contract.contractId}} + ({{contract.description}}) + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Contract Id + {{element.contractId}} + Base Token Id + {{element.baseTokenId}} + Opposite Token Id + {{element.oppositeTokenId}} + Base Token Count {{element.baseTokenCount}} Opposite Token Count {{element.oppositeTokenCount}} Operations +
+ Retire Tokens + local_fire_department +
+
+ View Details + visibility +
+
+
+
+ + +
+
+
\ No newline at end of file diff --git a/frontend/src/app/views/contract-request-config/contract-request-config.component.ts b/frontend/src/app/views/contract-request-config/contract-request-config.component.ts new file mode 100644 index 0000000000..65b7bd564e --- /dev/null +++ b/frontend/src/app/views/contract-request-config/contract-request-config.component.ts @@ -0,0 +1,155 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ProfileService } from 'src/app/services/profile.service'; +import { ContractService } from 'src/app/services/contract.service'; +import { forkJoin } from 'rxjs'; +import { VCViewerDialog } from 'src/app/schema-engine/vc-dialog/vc-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; + +/** + * Component for working with contract requests + */ +@Component({ + selector: 'contract-request-config', + templateUrl: './contract-request-config.component.html', + styleUrls: ['./contract-request-config.component.css'], +}) +export class ContractRequestConfigComponent implements OnInit, OnDestroy { + requests: any[] | null; + columns: string[] = []; + loading: boolean = true; + isConfirmed: boolean = false; + pageIndex: number; + pageSize: number; + requestsCount: any; + currentContract: any = ''; + + contracts: any[] | null = null; + + constructor( + private profileService: ProfileService, + private contractsService: ContractService, + private route: ActivatedRoute, + private router: Router, + private dialog: MatDialog, + ) { + this.requests = null; + this.pageIndex = 0; + this.pageSize = 100; + this.requestsCount = 0; + this.columns = [ + 'contractId', + 'baseTokenId', + 'oppositeTokenId', + 'baseTokenCount', + 'oppositeTokenCount', + 'retire', + ]; + } + + ngOnInit() { + this.loading = true; + this.currentContract = + this.route.snapshot.queryParams['contractId'] || ''; + this.loadContracts(); + } + + ngOnDestroy() {} + + loadContracts() { + this.requests = null; + this.isConfirmed = false; + this.loading = true; + forkJoin([ + this.profileService.getProfile(), + this.contractsService.all(), + ]).subscribe( + (value) => { + const profile = value[0]; + this.contracts = value[1]; + this.isConfirmed = !!(profile && profile.confirmed); + if (this.isConfirmed) { + this.loadAllContracts(); + } else { + setTimeout(() => { + this.loading = false; + }, 500); + } + }, + (e) => { + this.loading = false; + } + ); + } + + loadAllContracts() { + this.loading = true; + this.contractsService + .getRetireRequestsPage( + this.currentContract, + this.pageIndex, + this.pageSize + ) + .subscribe( + (policiesResponse) => { + this.requests = policiesResponse.body || []; + this.requestsCount = + policiesResponse.headers.get('X-Total-Count') || + this.requests.length; + setTimeout(() => { + this.loading = false; + }, 500); + }, + (e) => { + this.loading = false; + } + ); + } + + onPage(event: any) { + if (this.pageSize != event.pageSize) { + this.pageIndex = 0; + this.pageSize = event.pageSize; + } else { + this.pageIndex = event.pageIndex; + this.pageSize = event.pageSize; + } + this.loadAllContracts(); + } + + retireToken(requestId: string) { + this.loading = true; + this.contractsService.retireTokens(requestId).subscribe( + () => { + this.loading = false; + this.loadAllContracts(); + }, + () => (this.loading = false) + ); + } + + onFilter() { + if (this.currentContract) { + this.router.navigate(['/contracts/pairs'], { + queryParams: { + contractId: this.currentContract, + }, + }); + } else { + this.router.navigate(['/contracts/pairs']); + } + this.loadContracts(); + } + + viewRetireRequest(document: any) { + this.dialog.open(VCViewerDialog, { + width: '600px', + data: { + document: document.document, + title: 'View Retire Request Result', + type: 'VC', + viewDocument: true + } + }); + } +} \ No newline at end of file diff --git a/frontend/src/app/views/header/header.component.ts b/frontend/src/app/views/header/header.component.ts index 54de1bc45c..13aa139660 100644 --- a/frontend/src/app/views/header/header.component.ts +++ b/frontend/src/app/views/header/header.component.ts @@ -75,6 +75,14 @@ export class HeaderComponent implements OnInit { disabled: false, link: '/tokens' }, + { + name: "Contracts", + disabled: false, + link: '/contracts', + links: [ + '/contracts/pairs' + ] + }, { name: "Artifacts", disabled: false, @@ -241,7 +249,7 @@ export class HeaderComponent implements OnInit { return true; } if (link.links) { - return link.links.indexOf(this.activeLink) !== -1; + return link.links.indexOf(this.activeLink) !== -1 || link.links.indexOf(this.activeLinkRoot) !== -1; } if (link.pattern) { return link.pattern.test(this.activeLink); diff --git a/frontend/src/app/views/root-config/root-config.component.html b/frontend/src/app/views/root-config/root-config.component.html index 7b80eeb63d..6573cdc63b 100644 --- a/frontend/src/app/views/root-config/root-config.component.html +++ b/frontend/src/app/views/root-config/root-config.component.html @@ -82,7 +82,7 @@

Finish Setup

{{topic.topicId}} - {{topic.date}} + {{topic.date}}
diff --git a/frontend/src/app/views/schema-config/schema-config.component.html b/frontend/src/app/views/schema-config/schema-config.component.html index 3470cfa643..3fe672efbe 100644 --- a/frontend/src/app/views/schema-config/schema-config.component.html +++ b/frontend/src/app/views/schema-config/schema-config.component.html @@ -61,7 +61,7 @@ Topic - {{element.topicId}} + {{element.topicId}} @@ -226,4 +226,4 @@
-
\ No newline at end of file +
diff --git a/frontend/src/app/views/token-config/token-config.component.html b/frontend/src/app/views/token-config/token-config.component.html index 8a925a0818..dea37a18ba 100644 --- a/frontend/src/app/views/token-config/token-config.component.html +++ b/frontend/src/app/views/token-config/token-config.component.html @@ -27,7 +27,7 @@ Token Id - {{element.tokenId}} + {{element.tokenId}} @@ -174,4 +174,4 @@
- \ No newline at end of file + diff --git a/frontend/src/app/views/user-profile/user-profile.component.css b/frontend/src/app/views/user-profile/user-profile.component.css index aa199e2644..2401c5459d 100644 --- a/frontend/src/app/views/user-profile/user-profile.component.css +++ b/frontend/src/app/views/user-profile/user-profile.component.css @@ -85,7 +85,7 @@ form { } -.token-table { +.token-table, .retire-table { table-layout: fixed; width: 100%; min-width: 900px; @@ -279,4 +279,36 @@ a[disabled="true"]{ display: flex; flex-direction: column; margin-bottom: 20px; +} + +.cancel-action-btn, .view-action-btn { + color: #fff; + border-radius: 6px; + display: inline-block; + padding: 6px 0; + width: 200px; + margin: 0px 5px; + cursor: pointer; + font-weight: 500; + text-align: center; + -webkit-user-select: none; + user-select: none; + position: relative; + white-space: nowrap; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; +} + +.view-action-btn { + background: #4caf50; +} + +.cancel-action-btn { + background: darkred; +} + +.approved-request { + background-color: #d1f1c8; } \ No newline at end of file diff --git a/frontend/src/app/views/user-profile/user-profile.component.html b/frontend/src/app/views/user-profile/user-profile.component.html index 4d7ec2fd2e..8a994ac5ab 100644 --- a/frontend/src/app/views/user-profile/user-profile.component.html +++ b/frontend/src/app/views/user-profile/user-profile.component.html @@ -50,7 +50,7 @@ TOKEN - {{element.tokenName}} ({{element.tokenId}}) + {{element.tokenName}} ({{element.tokenId}}) @@ -100,6 +100,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Contract Id + {{element.contractId}} + Base Token Id + {{element.baseTokenId}} + Opposite Token Id + {{element.oppositeTokenId}} + Base Token Count {{element.baseTokenCount}} Opposite Token Count {{element.oppositeTokenCount}} Operations +
+ Cancel Request + cancel +
+
+ View Details + visibility +
+
+ +
+
@@ -153,4 +201,4 @@ (click)="retry()">Retry - \ No newline at end of file + diff --git a/frontend/src/app/views/user-profile/user-profile.component.ts b/frontend/src/app/views/user-profile/user-profile.component.ts index edd03498bf..5320b7a3b3 100644 --- a/frontend/src/app/views/user-profile/user-profile.component.ts +++ b/frontend/src/app/views/user-profile/user-profile.component.ts @@ -13,6 +13,8 @@ import { HeaderPropsService } from 'src/app/services/header-props.service'; import { InformService } from 'src/app/services/inform.service'; import { TasksService } from 'src/app/services/tasks.service'; import { WebSocketService } from 'src/app/services/web-socket.service'; +import { RetireTokenDialogComponent } from 'src/app/components/retire-token-dialog/retire-token-dialog.component'; +import { ContractService } from 'src/app/services/contract.service'; enum OperationMode { None, Generate, SetProfile, Associate @@ -41,6 +43,7 @@ export class UserProfileComponent implements OnInit { profile?: IUser | null; balance?: string | null; tokens?: Token[] | null; + contractRequests?: any[]; didDocument?: any; vcDocument?: any; standardRegistries?: IUser[]; @@ -61,6 +64,15 @@ export class UserProfileComponent implements OnInit { 'policies' ]; + displayedColumnsContractRequests: string[] = [ + 'contractId', + 'baseTokenId', + 'oppositeTokenId', + 'baseTokenCount', + 'oppositeTokenCount', + 'cancel' + ]; + private interval: any; hideVC: any; @@ -81,6 +93,7 @@ export class UserProfileComponent implements OnInit { private informService: InformService, private taskService: TasksService, private webSocketService: WebSocketService, + private contractService: ContractService, private fb: FormBuilder, public dialog: MatDialog, private headerProps: HeaderPropsService) { @@ -118,14 +131,17 @@ export class UserProfileComponent implements OnInit { this.profileService.getBalance(), this.tokenService.getTokens(), this.auth.getStandardRegistries(), - this.schemaService.getSystemSchemasByEntity(SchemaEntity.USER) + this.schemaService.getSystemSchemasByEntity(SchemaEntity.USER), + this.contractService.getRetireRequestsAll() ]).subscribe((value) => { this.profile = value[0] as IUser; this.balance = value[1] as string; this.tokens = value[2].map((e: any) => { return { ...new Token(e), - policies: e.policies + policies: e.policies, + serials: e.serials, + decimals: e.decimals } }); this.standardRegistries = value[3] || []; @@ -150,6 +166,7 @@ export class UserProfileComponent implements OnInit { this.schema = null; } + this.contractRequests = value[5] as any[]; setTimeout(() => { this.loading = false; this.headerProps.setLoading(false); @@ -281,6 +298,64 @@ export class UserProfileComponent implements OnInit { this.vcForm.updateValueAndValidity(); } + cancelContractRequest(id: string) { + this.loading = true; + this.contractService.cancelContractRequest(id).subscribe( + () => { + this.loadDate(); + this.loading = false; + }, + () => (this.loading = false) + ); + } + + createRetireRequest() { + const dialogRef = this.dialog.open(RetireTokenDialogComponent, { + width: '650px', + panelClass: 'g-dialog', + disableClose: true, + autoFocus: false, + data: { + tokens: this.tokens, + }, + }); + this.loading = false; + dialogRef.afterClosed().subscribe(async (result) => { + if (result) { + this.loading = true; + this.contractService + .createRetireRequest( + result.contractId, + result.baseTokenId, + result.oppositeTokenId, + result.baseTokenCount, + result.oppositeTokenCount, + result.baseTokenSerials, + result.oppositeTokenSerials + ) + .subscribe( + () => { + this.loadDate(); + this.loading = false; + }, + () => (this.loading = false) + ); + } + }); + } + + viewRetireRequest(document: any) { + this.dialog.open(VCViewerDialog, { + width: '600px', + data: { + document: document.document, + title: 'View Retire Request Result', + type: 'VC', + viewDocument: true + } + }); + } + onAsyncError(error: any) { this.informService.processAsyncError(error); this.loading = false; diff --git a/frontend/src/environments/environment.demo.ts b/frontend/src/environments/environment.demo.ts index 46c81d639e..aecff7f39d 100644 --- a/frontend/src/environments/environment.demo.ts +++ b/frontend/src/environments/environment.demo.ts @@ -1,4 +1,17 @@ export const environment = { production: true, - displayDemoAccounts: true + displayDemoAccounts: true, + explorerSettings: { + url: 'https://explore.lworks.io/${network}/${type}/${value}', + networkMap: { + 'mainnet': 'mainnet', + 'testnet': 'testnet', + 'local': 'testnet' + }, + typeMap: { + 'tokens': 'tokens', + 'topics': 'topics', + 'accounts': 'accounts' + } + } }; diff --git a/frontend/src/environments/environment.prod.ts b/frontend/src/environments/environment.prod.ts index 82234e7256..d3ec18e615 100644 --- a/frontend/src/environments/environment.prod.ts +++ b/frontend/src/environments/environment.prod.ts @@ -1,4 +1,17 @@ export const environment = { production: true, - displayDemoAccounts: false + displayDemoAccounts: false, + explorerSettings: { + url: 'https://explore.lworks.io/${network}/${type}/${value}', + networkMap: { + 'mainnet': 'mainnet', + 'testnet': 'testnet', + 'local': 'testnet' + }, + typeMap: { + 'tokens': 'tokens', + 'topics': 'topics', + 'accounts': 'accounts' + } + } }; diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index 6bf7e37532..e664325f6f 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -4,7 +4,20 @@ export const environment = { production: false, - displayDemoAccounts: true + displayDemoAccounts: true, + explorerSettings: { + url: 'https://explore.lworks.io/${network}/${type}/${value}', + networkMap: { + 'mainnet': 'mainnet', + 'testnet': 'testnet', + 'local': 'testnet' + }, + typeMap: { + 'tokens': 'tokens', + 'topics': 'topics', + 'accounts': 'accounts' + } + } }; /* diff --git a/guardian-service/.env b/guardian-service/.env index 155fd00821..81f198d7ea 100644 --- a/guardian-service/.env +++ b/guardian-service/.env @@ -13,3 +13,5 @@ MESSAGE_LANG="en-US" LOG_LEVEL="1" SEND_KEYS_TO_VAULT="TRUE" MULTI_POLICY_SCHEDULER="0 0 * * *" +CONTRACT_FILE_ID="0.0.49165176" +#MQ_MESSAGE_CHUNK=5000000 \ No newline at end of file diff --git a/guardian-service/.env.docker b/guardian-service/.env.docker index 49fc183057..88e61b8639 100644 --- a/guardian-service/.env.docker +++ b/guardian-service/.env.docker @@ -12,4 +12,6 @@ HEDERA_NET="testnet" MESSAGE_LANG="en-US" LOG_LEVEL="1" SEND_KEYS_TO_VAULT="TRUE" -MULTI_POLICY_SCHEDULER="0 0 * * *" \ No newline at end of file +MULTI_POLICY_SCHEDULER="0 0 * * *" +CONTRACT_FILE_ID="0.0.49165176" +#MQ_MESSAGE_CHUNK=5000000 \ No newline at end of file diff --git a/guardian-service/package.json b/guardian-service/package.json index a95ae8d932..a7762eed2b 100644 --- a/guardian-service/package.json +++ b/guardian-service/package.json @@ -12,9 +12,9 @@ }, "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.7.0", - "@guardian/interfaces": "^2.7.0", - "@hashgraph/sdk": "^2.18.3", + "@guardian/common": "^2.8.0-prerelease", + "@guardian/interfaces": "^2.8.0-prerelease", + "@hashgraph/sdk": "^2.19.0", "@mikro-orm/core": "~5.3.0", "@mikro-orm/mongodb": "~5.3.0", "@transmute/credentials-context": "^0.7.0-unstable.60", @@ -74,5 +74,6 @@ "test:local": "mocha tests/**/*.test.js", "test:stability": "mocha tests/stability.test.js" }, - "version": "2.7.0" + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0" } diff --git a/guardian-service/src/api/contract.service.ts b/guardian-service/src/api/contract.service.ts new file mode 100644 index 0000000000..f1f3e7a95c --- /dev/null +++ b/guardian-service/src/api/contract.service.ts @@ -0,0 +1,811 @@ +import { ApiResponse } from '@api/api-response'; +import { + MessageBrokerChannel, + MessageResponse, + MessageError, + Logger, + DataBaseHelper, +} from '@guardian/common'; +import { + ContractStatus, + MessageAPI, + Schema, + SchemaEntity, + SchemaHelper, + TopicType, + WorkerTaskType, +} from '@guardian/interfaces'; +import { Contract } from '@entity/contract'; +import { Workers } from '@helpers/workers'; +import { Users } from '@helpers/users'; +import { KeyType, Wallet } from '@helpers/wallet'; +import { DatabaseServer } from '@database-modules'; +import { RetireRequest } from '@entity/retire-request'; +import { Schema as SchemaCollection } from '@entity/schema'; +import { + MessageAction, + MessageServer, + TopicConfig, + TopicHelper, + VCMessage, +} from '@hedera-modules'; +import { Topic } from '@entity/topic'; +import { publishSystemSchema } from './schema.service'; +import { VcHelper } from '@helpers/vc-helper'; +import { VcDocument as VcDocumentCollection } from '@entity/vc-document'; + +/** + * Connect to the message broker methods of working with contracts. + * + * @param channel - channel + */ +export async function contractAPI( + channel: MessageBrokerChannel, + contractRepository: DataBaseHelper, + retireRequestRepository: DataBaseHelper +): Promise { + ApiResponse(channel, MessageAPI.GET_CONTRACT, async (msg) => { + try { + if (!msg) { + return new MessageError('Invalid get contract parameters'); + } + + const { pageIndex, pageSize, owner } = msg; + + const otherOptions: any = {}; + const _pageSize = parseInt(pageSize, 10); + const _pageIndex = parseInt(pageIndex, 10); + if (Number.isInteger(_pageSize) && Number.isInteger(_pageIndex)) { + otherOptions.orderBy = { createDate: 'DESC' }; + otherOptions.limit = Math.min(100, _pageSize); + otherOptions.offset = _pageIndex * _pageSize; + } else { + otherOptions.limit = 100; + } + + return new MessageResponse( + await contractRepository.findAndCount( + { + owner, + }, + otherOptions + ) + ); + } catch (error) { + new Logger().error(error, ['GUARDIAN_SERVICE']); + return new MessageError(error); + } + }); + + ApiResponse(channel, MessageAPI.GET_RETIRE_REQUEST, async (msg) => { + try { + if (!msg) { + return new MessageError('Invalid get contract parameters'); + } + + const { pageIndex, pageSize, owner, contractId, did } = msg; + + const filters: any = {}; + if (owner) { + filters.owner = owner; + } + if (contractId) { + const contracts = await contractRepository.findOne({ + owner: did + }); + if (contracts.owner !== did) { + throw new Error('You are not contract owner'); + } + filters.contractId = contractId; + } else { + const contracts = await contractRepository.find({ + owner: did + }); + filters.contractId = { + $in: contracts.map(contract => contract.contractId) + } + } + + const otherOptions: any = {}; + const _pageSize = parseInt(pageSize, 10); + const _pageIndex = parseInt(pageIndex, 10); + if (Number.isInteger(_pageSize) && Number.isInteger(_pageIndex)) { + otherOptions.orderBy = { createDate: 'DESC' }; + otherOptions.limit = Math.min(100, _pageSize); + otherOptions.offset = _pageIndex * _pageSize; + } else { + otherOptions.limit = 100; + } + + const retireRequestsAndCount = + await retireRequestRepository.findAndCount( + filters, + otherOptions + ); + + for (const retireRequest of retireRequestsAndCount[0] as any[]) { + if (retireRequest.vcDocumentHash) { + retireRequest.vcDocument = await new DataBaseHelper( + VcDocumentCollection + ).findOne({ + hash: retireRequest.vcDocumentHash, + }); + } + } + + return new MessageResponse(retireRequestsAndCount); + } catch (error) { + new Logger().error(error, ['GUARDIAN_SERVICE']); + return new MessageError(error); + } + }); + + ApiResponse(channel, MessageAPI.CREATE_CONTRACT, async (msg) => { + try { + if (!msg) { + return new MessageError('Invalid get contract parameters'); + } + + const { description, did } = msg; + + const users = new Users(); + const wallet = new Wallet(); + const workers = new Workers(); + const root = await users.getUserById(did); + const rootKey = await wallet.getKey( + root.walletToken, + KeyType.KEY, + did + ); + + const contractId = await workers.addNonRetryableTask( + { + type: WorkerTaskType.CREATE_CONTRACT, + data: { + bytecodeFileId: process.env.CONTRACT_FILE_ID, + hederaAccountId: root.hederaAccountId, + hederaAccountKey: rootKey, + topicKey: rootKey, + }, + }, + 1 + ); + + return new MessageResponse( + await contractRepository.save({ + contractId, + status: ContractStatus.APPROVED, + owner: did, + isOwnerCreator: true, + description, + }) + ); + } catch (error) { + new Logger().error(error, ['GUARDIAN_SERVICE']); + return new MessageError(error); + } + }); + + ApiResponse(channel, MessageAPI.CHECK_CONTRACT_STATUS, async (msg) => { + try { + if (!msg) { + return new MessageError('Invalid get contract parameters'); + } + + const { did, contractId } = msg; + + if (!contractId) { + throw new Error('Invalid contract identifier'); + } + + const users = new Users(); + const wallet = new Wallet(); + const workers = new Workers(); + const root = await users.getUserById(did); + const rootKey = await wallet.getKey( + root.walletToken, + KeyType.KEY, + did + ); + + const checkStatusResult = await workers.addNonRetryableTask( + { + type: WorkerTaskType.CHECK_STATUS, + data: { + contractId, + hederaAccountId: root.hederaAccountId, + hederaAccountKey: rootKey, + }, + }, + 1 + ); + + if (checkStatusResult) { + await contractRepository.update( + { + status: ContractStatus.APPROVED, + }, + { + contractId, + owner: did, + } + ); + } + return new MessageResponse(checkStatusResult); + } catch (error) { + new Logger().error(error, ['GUARDIAN_SERVICE']); + return new MessageError(error); + } + }); + + ApiResponse(channel, MessageAPI.ADD_CONTRACT_USER, async (msg) => { + try { + if (!msg) { + return new MessageError('Invalid get contract parameters'); + } + + const { userId, contractId, did } = msg; + + if (!contractId) { + throw new Error('Invalid contract identifier'); + } + if (!userId) { + throw new Error('Invalid user identifier') + } + + const users = new Users(); + const wallet = new Wallet(); + const workers = new Workers(); + const root = await users.getUserById(did); + const rootKey = await wallet.getKey( + root.walletToken, + KeyType.KEY, + did + ); + + return new MessageResponse( + await workers.addNonRetryableTask( + { + type: WorkerTaskType.ADD_CONTRACT_USER, + data: { + contractId, + hederaAccountId: root.hederaAccountId, + hederaAccountKey: rootKey, + userId, + }, + }, + 1 + ) + ); + } catch (error) { + new Logger().error(error, ['GUARDIAN_SERVICE']); + return new MessageError(error); + } + }); + + ApiResponse(channel, MessageAPI.ADD_CONTRACT_PAIR, async (msg) => { + try { + if (!msg) { + return new MessageError('Invalid add contract pair parameters'); + } + + const { + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount, + contractId, + did, + } = msg; + + const users = new Users(); + const wallet = new Wallet(); + const workers = new Workers(); + const root = await users.getUserById(did); + const rootKey = await wallet.getKey( + root.walletToken, + KeyType.KEY, + did + ); + + const baseToken = await new DatabaseServer().getTokenById( + baseTokenId + ); + const oppositeToken = await new DatabaseServer().getTokenById( + oppositeTokenId + ); + + const grantKycKeys = []; + const baseTokenKycKey = await wallet.getUserKey( + did, + KeyType.TOKEN_KYC_KEY, + baseTokenId + ); + if (baseTokenKycKey) { + grantKycKeys.push(baseTokenKycKey); + } + const oppositeTokenKycKey = await wallet.getUserKey( + did, + KeyType.TOKEN_KYC_KEY, + oppositeTokenId + ); + if (oppositeTokenKycKey) { + grantKycKeys.push(oppositeTokenKycKey); + } + return new MessageResponse( + await workers.addNonRetryableTask( + { + type: WorkerTaskType.ADD_CONTRACT_PAIR, + data: { + contractId, + hederaAccountId: root.hederaAccountId, + hederaAccountKey: rootKey, + baseTokenId, + oppositeTokenId, + baseTokenCount: baseToken.decimals + ? Math.pow(10, baseToken.decimals) * + baseTokenCount + : baseTokenCount, + oppositeTokenCount: oppositeToken.decimals + ? Math.pow(10, oppositeToken.decimals) * + oppositeTokenCount + : oppositeTokenCount, + grantKycKeys + }, + }, + 1 + ) + ); + } catch (error) { + new Logger().error(error, ['GUARDIAN_SERVICE']); + return new MessageError(error); + } + }); + + ApiResponse(channel, MessageAPI.IMPORT_CONTRACT, async (msg) => { + try { + if (!msg) { + return new MessageError('Invalid contract identifier'); + } + const { contractId, did, description } = msg; + + const users = new Users(); + const wallet = new Wallet(); + const workers = new Workers(); + const root = await users.getUserById(did); + const rootKey = await wallet.getKey( + root.walletToken, + KeyType.KEY, + did + ); + + const contractOwner = await workers.addNonRetryableTask( + { + type: WorkerTaskType.GET_CONTRACT_INFO, + data: { + contractId, + hederaAccountId: root.hederaAccountId, + hederaAccountKey: rootKey, + }, + }, + 1 + ); + + return new MessageResponse( + await contractRepository.save( + { + contractId, + owner: did, + isOwnerCreator: contractOwner === root.hederaAccountId, + description, + status: + contractOwner === root.hederaAccountId + ? ContractStatus.APPROVED + : ContractStatus.WAIT, + }, + { + contractId, + owner: did, + } + ) + ); + } catch (error) { + new Logger().error(error, ['GUARDIAN_SERVICE']); + return new MessageError(error); + } + }); + + ApiResponse(channel, MessageAPI.GET_CONTRACT_PAIR, async (msg) => { + try { + if (!msg) { + return new MessageError('Invalid add contract pair parameters'); + } + + const { baseTokenId, oppositeTokenId, did, owner } = msg; + + const users = new Users(); + const wallet = new Wallet(); + const workers = new Workers(); + const root = await users.getUserById(did); + const rootKey = await wallet.getKey( + root.walletToken, + KeyType.KEY, + did + ); + const baseToken = await new DatabaseServer().getTokenById( + baseTokenId + ); + const oppositeToken = await new DatabaseServer().getTokenById( + oppositeTokenId + ); + const contracts = await contractRepository.find({ + owner, + }); + const contractPairs = []; + for (const contract of contracts) { + const contractPair = await workers.addNonRetryableTask( + { + type: WorkerTaskType.GET_CONTRACT_PAIR, + data: { + contractId: contract.contractId, + hederaAccountId: root.hederaAccountId, + hederaAccountKey: rootKey, + baseTokenId, + oppositeTokenId, + }, + }, + 1 + ); + contractPairs.push({ + baseTokenRate: baseToken.decimals + ? contractPair.baseTokenRate / + Math.pow(10, baseToken.decimals) + : contractPair.baseTokenRate, + oppositeTokenRate: oppositeToken.decimals + ? contractPair.oppositeTokenRate / + Math.pow(10, oppositeToken.decimals) + : contractPair.oppositeTokenRate, + contractId: contractPair.contractId, + description: contract.description, + }); + } + + return new MessageResponse(contractPairs); + } catch (error) { + new Logger().error(error, ['GUARDIAN_SERVICE']); + return new MessageError(error); + } + }); + + ApiResponse(channel, MessageAPI.ADD_RETIRE_REQUEST, async (msg) => { + try { + if (!msg) { + return new MessageError('Invalid add contract pair parameters'); + } + + const { + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount, + baseTokenSerials, + oppositeTokenSerials, + contractId, + did, + } = msg; + + const users = new Users(); + const wallet = new Wallet(); + const workers = new Workers(); + const root = await users.getUserById(did); + const rootKey = await wallet.getKey( + root.walletToken, + KeyType.KEY, + did + ); + + const baseToken = await new DatabaseServer().getTokenById( + baseTokenId + ); + const oppositeToken = await new DatabaseServer().getTokenById( + oppositeTokenId + ); + + const addRequestResult = await workers.addNonRetryableTask( + { + type: WorkerTaskType.ADD_RETIRE_REQUEST, + data: { + contractId, + hederaAccountId: root.hederaAccountId, + hederaAccountKey: rootKey, + baseTokenId, + oppositeTokenId, + baseTokenCount: baseToken.decimals + ? Math.pow(10, baseToken.decimals) * baseTokenCount + : baseTokenCount, + oppositeTokenCount: oppositeToken.decimals + ? Math.pow(10, oppositeToken.decimals) * + oppositeTokenCount + : oppositeTokenCount, + baseTokenSerials, + oppositeTokenSerials, + }, + }, + 1 + ); + + const contractRequest = await workers.addNonRetryableTask( + { + type: WorkerTaskType.GET_RETIRE_REQUEST, + data: { + contractId, + hederaAccountId: root.hederaAccountId, + hederaAccountKey: rootKey, + baseTokenId, + oppositeTokenId, + userId: root.hederaAccountId, + }, + }, + 1 + ); + + await retireRequestRepository.save( + { + contractId, + baseTokenId, + oppositeTokenId, + owner: did, + baseTokenCount: baseToken.decimals + ? contractRequest.baseTokenCount / + Math.pow(10, baseToken.decimals) + : contractRequest.baseTokenCount, + oppositeTokenCount: oppositeToken.decimals + ? contractRequest.oppositeTokenCount / + Math.pow(10, oppositeToken.decimals) + : contractRequest.oppositeTokenCount, + baseTokenSerials, + oppositeTokenSerials, + }, + { + contractId, + baseTokenId, + oppositeTokenId, + owner: did, + vcDocumentHash: null + } + ); + + return new MessageResponse(addRequestResult); + } catch (error) { + new Logger().error(error, ['GUARDIAN_SERVICE']); + return new MessageError(error); + } + }); + + ApiResponse(channel, MessageAPI.CANCEL_RETIRE_REQUEST, async (msg) => { + try { + if (!msg) { + return new MessageError('Invalid add contract pair parameters'); + } + + const { requestId, did } = msg; + + const retireRequest = await retireRequestRepository.findOne({ + id: requestId, + }); + + if (did !== retireRequest.owner) { + throw new Error('You are not owner of retire request'); + } + + const users = new Users(); + const wallet = new Wallet(); + const workers = new Workers(); + const root = await users.getUserById(did); + const rootKey = await wallet.getKey( + root.walletToken, + KeyType.KEY, + did + ); + + const cancelResult = await workers.addNonRetryableTask( + { + type: WorkerTaskType.CANCEL_RETIRE_REQUEST, + data: { + contractId: retireRequest.contractId, + hederaAccountId: root.hederaAccountId, + hederaAccountKey: rootKey, + baseTokenId: retireRequest.baseTokenId, + oppositeTokenId: retireRequest.oppositeTokenId, + }, + }, + 1 + ); + + if (cancelResult) { + await retireRequestRepository.remove(retireRequest); + } + + return new MessageResponse(cancelResult); + } catch (error) { + new Logger().error(error, ['GUARDIAN_SERVICE']); + return new MessageError(error); + } + }); + + ApiResponse(channel, MessageAPI.RETIRE_TOKENS, async (msg) => { + try { + if (!msg) { + return new MessageError('Invalid add contract pair parameters'); + } + + const { requestId, did } = msg; + + const retireRequest = await retireRequestRepository.findOne({ + id: requestId, + }); + + const users = new Users(); + const wallet = new Wallet(); + const workers = new Workers(); + const root = await users.getUserById(did); + const rootKey = await wallet.getKey( + root.walletToken, + KeyType.KEY, + did + ); + const retireRequestUser = await users.getUserById( + retireRequest.owner + ); + const retireRequestOwner = await users.getUserById( + retireRequest.owner + ); + const wipeKeys = []; + wipeKeys.push( + await wallet.getUserKey( + did, + KeyType.TOKEN_WIPE_KEY, + retireRequest.baseTokenId + ) + ); + wipeKeys.push( + await wallet.getUserKey( + did, + KeyType.TOKEN_WIPE_KEY, + retireRequest.oppositeTokenId + ) + ); + + const retireResult = await workers.addNonRetryableTask( + { + type: WorkerTaskType.RETIRE_TOKENS, + data: { + contractId: retireRequest.contractId, + hederaAccountId: root.hederaAccountId, + hederaAccountKey: rootKey, + baseTokenId: retireRequest.baseTokenId, + oppositeTokenId: retireRequest.oppositeTokenId, + userId: retireRequestUser.hederaAccountId, + wipeKeys, + }, + }, + 1 + ); + + let topicConfig = await TopicConfig.fromObject( + await new DataBaseHelper(Topic).findOne({ + owner: did, + type: TopicType.RetireTopic, + }), + true + ); + const parent = await TopicConfig.fromObject( + await DatabaseServer.getTopicByType(did, TopicType.UserTopic), + true + ); + if (!topicConfig) { + const topicHelper = new TopicHelper( + root.hederaAccountId, + rootKey + ); + topicConfig = await topicHelper.create( + { + type: TopicType.RetireTopic, + name: TopicType.RetireTopic, + description: TopicType.RetireTopic, + owner: did, + }, + { + admin: false, + submit: true, + } + ); + await topicConfig.saveKeys(); + await topicHelper.twoWayLink(topicConfig, parent, null); + await new DataBaseHelper(Topic).save(topicConfig.toObject()); + } + + let schema: SchemaCollection = null; + + schema = await new DataBaseHelper(SchemaCollection).findOne({ + entity: SchemaEntity.RETIRE_TOKEN, + readonly: true, + topicId: topicConfig.topicId, + }); + const messageServer = new MessageServer( + root.hederaAccountId, + rootKey + ); + messageServer.setTopicObject(topicConfig); + if (!schema) { + schema = await new DataBaseHelper(SchemaCollection).findOne({ + entity: SchemaEntity.RETIRE_TOKEN, + system: true, + active: true, + }); + if (schema) { + schema.creator = did; + schema.owner = did; + const item = await publishSystemSchema( + schema, + messageServer, + MessageAction.PublishSystemSchema + ); + await new DataBaseHelper(SchemaCollection).save(item); + } + } + + const schemaObject = new Schema(schema); + const vcHelper = new VcHelper(); + + let credentialSubject: any = { + baseTokenId: retireRequest.baseTokenId, + oppositeTokenId: retireRequest.oppositeTokenId, + baseTokenCount: retireRequest.baseTokenCount, + oppositeTokenCount: retireRequest.oppositeTokenCount, + userId: retireRequestOwner.hederaAccountId, + baseTokenSerials: retireRequest.baseTokenSerials, + oppositeTokenSerials: retireRequest.oppositeTokenSerials, + }; + credentialSubject.id = did; + + if (schemaObject) { + credentialSubject = SchemaHelper.updateObjectContext( + schemaObject, + credentialSubject + ); + } + + const vcObject = await vcHelper.createVC( + did, + rootKey, + credentialSubject + ); + const vcMessage = new VCMessage(MessageAction.CreateVC); + vcMessage.setDocument(vcObject); + await messageServer + .sendMessage(vcMessage); + + const vcDoc = await new DataBaseHelper(VcDocumentCollection).save({ + hash: vcMessage.hash, + owner: did, + document: vcMessage.document, + type: schemaObject?.entity, + }); + + await retireRequestRepository.update( + { + vcDocumentHash: vcDoc.hash, + }, + { + id: retireRequest.id, + } + ); + + return new MessageResponse(retireResult); + } catch (error) { + new Logger().error(error, ['GUARDIAN_SERVICE']); + return new MessageError(error); + } + }); +} \ No newline at end of file diff --git a/guardian-service/src/api/schema.service.ts b/guardian-service/src/api/schema.service.ts index 079618d332..8f1a3c7f9e 100644 --- a/guardian-service/src/api/schema.service.ts +++ b/guardian-service/src/api/schema.service.ts @@ -79,12 +79,15 @@ export async function setDefaultSchema() { await fn(map[SchemaEntity.MINT_NFTOKEN]); await fn(map[SchemaEntity.MINT_TOKEN]); + await fn(map[SchemaEntity.RETIRE_TOKEN]); await fn(map[SchemaEntity.POLICY]); await fn(map[SchemaEntity.STANDARD_REGISTRY]); await fn(map[SchemaEntity.WIPE_TOKEN]); await fn(map[SchemaEntity.ISSUER]); await fn(map[SchemaEntity.USER_ROLE]); await fn(map[SchemaEntity.CHUNK]); + await fn(map[SchemaEntity.ACTIVITY_IMPACT]); + await fn(map[SchemaEntity.TOKEN_DATA_SOURCE]); } /** @@ -432,11 +435,13 @@ export async function publishSchema( * @param item * @param messageServer * @param type + * @param notifier */ export async function publishSystemSchema( item: SchemaCollection, messageServer: MessageServer, - type?: MessageAction + type?: MessageAction, + notifier?: INotifier ): Promise { delete item.id; delete item._id; @@ -446,7 +451,43 @@ export async function publishSystemSchema( item.version = undefined; item.topicId = messageServer.getTopic(); SchemaHelper.setVersion(item, undefined, undefined); - return await publishSchema(item, messageServer, type); + const result = await publishSchema(item, messageServer, type); + if (notifier) { + notifier.info(`Schema ${result.name || '-'} published`); + } + return result; +} + +/** + * Publish system schemas + * @param systemSchemas + * @param messageServer + * @param owner + * @param notifier + */ +export async function publishSystemSchemas( + systemSchemas: SchemaCollection[], + messageServer: MessageServer, + owner: string, + notifier: INotifier +): Promise { + const tasks = []; + for (const schema of systemSchemas) { + if (schema) { + schema.creator = owner; + schema.owner = owner; + tasks.push(publishSystemSchema( + schema, + messageServer, + MessageAction.PublishSystemSchema, + notifier + )); + } + } + const items = await Promise.all(tasks); + for (const schema of items) { + await DatabaseServer.createAndSaveSchema(schema); + } } /** diff --git a/guardian-service/src/api/token.service.ts b/guardian-service/src/api/token.service.ts index f6134ba5f6..fe23e3f3a9 100644 --- a/guardian-service/src/api/token.service.ts +++ b/guardian-service/src/api/token.service.ts @@ -13,7 +13,7 @@ import { Workers } from '@helpers/workers'; * @param info * @param token */ -function getTokenInfo(info: any, token: any) { +function getTokenInfo(info: any, token: any, serials?: any[]) { const tokenId = token.tokenId; const result: any = { id: token.id, @@ -31,7 +31,8 @@ function getTokenInfo(info: any, token: any) { balance: null, hBarBalance: null, frozen: null, - kyc: null + kyc: null, + serials } if (info && info[tokenId]) { result.associated = true; @@ -750,9 +751,21 @@ export async function tokenAPI( : {} ); + const serials = + (await workers.addNonRetryableTask( + { + type: WorkerTaskType.GET_USER_NFTS_SERIALS, + data: { + operatorId: userID, + operatorKey: userKey, + }, + }, + 1 + )) || {}; + const result: any[] = []; for (const token of tokens) { - result.push(getTokenInfo(info, token)); + result.push(getTokenInfo(info, token, serials[token.tokenId])); } return new MessageResponse(result); } catch (error) { diff --git a/guardian-service/src/app.ts b/guardian-service/src/app.ts index f57ce9cf45..36f4cde481 100644 --- a/guardian-service/src/app.ts +++ b/guardian-service/src/app.ts @@ -46,6 +46,9 @@ import { artifactAPI } from '@api/artifact.service'; import { Policy } from '@entity/policy'; import { sendKeysToVault } from '@helpers/send-keys-to-vault'; import { SynchronizationService } from '@policy-engine/multi-policy-service'; +import { Contract } from '@entity/contract'; +import { contractAPI } from '@api/contract.service'; +import { RetireRequest } from '@entity/retire-request'; export const obj = {}; @@ -159,6 +162,8 @@ Promise.all([ const settingsRepository = new DataBaseHelper(Settings); const topicRepository = new DataBaseHelper(Topic); const policyRepository = new DataBaseHelper(Policy); + const contractRepository = new DataBaseHelper(Contract); + const retireRequestRepository = new DataBaseHelper(RetireRequest); state.updateState(ApplicationStates.INITIALIZING); @@ -172,6 +177,7 @@ Promise.all([ await demoAPI(channel, apiGatewayChannel, settingsRepository); await trustChainAPI(channel, didDocumentRepository, vcDocumentRepository, vpDocumentRepository); await artifactAPI(channel); + await contractAPI(channel, contractRepository, retireRequestRepository); } catch (error) { console.error(error.message); process.exit(0); diff --git a/guardian-service/src/database-modules/database-server.ts b/guardian-service/src/database-modules/database-server.ts index 48cd1440e2..516c9b2995 100644 --- a/guardian-service/src/database-modules/database-server.ts +++ b/guardian-service/src/database-modules/database-server.ts @@ -825,11 +825,15 @@ export class DatabaseServer { * @param countResult * @virtual */ - public async getVcDocuments(filters: any, options?: any, countResult?: boolean): Promise { + public async getVcDocuments( + filters: any, + options?: any, + countResult?: boolean + ): Promise { if (countResult) { - return await this.count(VcDocumentCollection, filters, options); + return await this.count(VcDocumentCollection, filters, options) as T; } - return await this.find(VcDocumentCollection, filters, options); + return await this.find(VcDocumentCollection, filters, options) as T; } /** @@ -840,11 +844,15 @@ export class DatabaseServer { * @param countResult * @virtual */ - public async getVpDocuments(filters: any, options?: any, countResult?: boolean): Promise { + public async getVpDocuments( + filters: any, + options?: any, + countResult?: boolean + ): Promise { if (countResult) { - return await this.count(VpDocumentCollection, filters, options); + return await this.count(VpDocumentCollection, filters, options) as T; } - return await this.find(VpDocumentCollection, filters, options); + return await this.find(VpDocumentCollection, filters, options) as T; } /** @@ -2079,7 +2087,7 @@ export class DatabaseServer { * Create MultiPolicyTransaction * @param transaction */ - public static async createMultiPolicyTransaction(transaction:any): Promise { + public static async createMultiPolicyTransaction(transaction: any): Promise { const item = new DataBaseHelper(MultiPolicyTransaction).create(transaction); return await new DataBaseHelper(MultiPolicyTransaction).save(item); } diff --git a/guardian-service/src/entity/contract.ts b/guardian-service/src/entity/contract.ts new file mode 100644 index 0000000000..6e31b51630 --- /dev/null +++ b/guardian-service/src/entity/contract.ts @@ -0,0 +1,47 @@ +import { BeforeCreate, Entity, Property } from '@mikro-orm/core'; +import { BaseEntity } from '@guardian/common'; +import { ContractStatus } from '@guardian/interfaces'; + +/** + * Contract collection + */ +@Entity() +export class Contract extends BaseEntity { + /** + * Hedera Contract Id + */ + @Property({ nullable: true }) + contractId?: string; + + /** + * Description + */ + @Property({ nullable: true }) + description?: string; + + /** + * Owner + */ + @Property({ nullable: true }) + owner?: string; + + /** + * Creator + */ + @Property({ default: false }) + isOwnerCreator: boolean = false; + + /** + * Contract status + */ + @Property({ nullable: true }) + status?: ContractStatus; + + /** + * Contract defaults + */ + @BeforeCreate() + setDefaults() { + this.status = this.status || ContractStatus.WAIT; + } +} diff --git a/guardian-service/src/entity/retire-request.ts b/guardian-service/src/entity/retire-request.ts new file mode 100644 index 0000000000..fddbf0af1b --- /dev/null +++ b/guardian-service/src/entity/retire-request.ts @@ -0,0 +1,62 @@ +import { Entity, Property } from '@mikro-orm/core'; +import { BaseEntity } from '@guardian/common'; + +/** + * Retire Request + */ +@Entity() +export class RetireRequest extends BaseEntity { + /** + * Hedera Contract Id + */ + @Property({ nullable: true }) + contractId?: string; + + /** + * Base Token Id + */ + @Property({ nullable: true }) + baseTokenId?: string; + + /** + * Owner + */ + @Property({ nullable: true }) + owner?: string; + + /** + * Opposite Token Id + */ + @Property({ nullable: true }) + oppositeTokenId?: string; + + /** + * Base Token Count + */ + @Property({ nullable: true }) + baseTokenCount?: number; + + /** + * Opposite Token Count + */ + @Property({ nullable: true }) + oppositeTokenCount?: number; + + /** + * Base Token Count + */ + @Property({ nullable: true }) + baseTokenSerials?: number[]; + + /** + * Opposite Token Count + */ + @Property({ nullable: true }) + oppositeTokenSerials?: number[]; + + /** + * Vc Document Hash + */ + @Property({ nullable: true }) + vcDocumentHash?: string; +} \ No newline at end of file diff --git a/guardian-service/src/entity/token.ts b/guardian-service/src/entity/token.ts index 7fbac9da6a..76b73d3bb9 100644 --- a/guardian-service/src/entity/token.ts +++ b/guardian-service/src/entity/token.ts @@ -1,4 +1,4 @@ -import { IToken } from '@guardian/interfaces'; +import { IToken, TokenType } from '@guardian/interfaces'; import { Entity, Property, Unique } from '@mikro-orm/core'; import { BaseEntity } from '@guardian/common'; @@ -30,7 +30,7 @@ export class Token extends BaseEntity implements IToken { * Token type */ @Property({ nullable: true }) - tokenType?: string; + tokenType?: TokenType; /** * Token decimals diff --git a/guardian-service/src/policy-engine/blocks/custom-logic-block.ts b/guardian-service/src/policy-engine/blocks/custom-logic-block.ts index a1653f8530..1e0243286b 100644 --- a/guardian-service/src/policy-engine/blocks/custom-logic-block.ts +++ b/guardian-service/src/policy-engine/blocks/custom-logic-block.ts @@ -1,10 +1,11 @@ +import { Worker } from 'node:worker_threads'; +import path from 'path' import { ActionCallback, BasicBlock } from '@policy-engine/helpers/decorators'; import { CatchErrors } from '@policy-engine/helpers/decorators/catch-errors'; import { PolicyComponentsUtils } from '@policy-engine/policy-components-utils'; import { IPolicyCalculateBlock, IPolicyDocument, IPolicyEventState } from '@policy-engine/policy-engine.interface'; import { VcHelper } from '@helpers/vc-helper'; import { ArtifactType, GenerateUUIDv4, SchemaHelper } from '@guardian/interfaces'; -import * as mathjs from 'mathjs'; import { IPolicyEvent, PolicyInputEventType, PolicyOutputEventType } from '@policy-engine/interfaces'; import { ChildrenType, ControlType } from '@policy-engine/interfaces/block-about'; import { IPolicyUser } from '@policy-engine/policy-user'; @@ -182,15 +183,33 @@ export class CustomLogicBlock { for (const execCodeArtifact of execCodeArtifacts) { execCode += (await DatabaseServer.getArtifactFileByUUID(execCodeArtifact.uuid)).toString(); } - const func = Function(`const [done, user, documents, mathjs, artifacts] = arguments;${execCode}${ref.options.expression}`); const artifacts = []; const jsonArtifacts = ref.options.artifacts?.filter(artifact => artifact.type === ArtifactType.JSON) || []; for (const jsonArtifact of jsonArtifacts) { - artifacts.push(JSON.parse((await DatabaseServer.getArtifactFileByUUID(jsonArtifact.uuid)).toString())); } - func.apply(documents, [done, user, documents, mathjs, artifacts]); + + const worker = new Worker(path.join(path.dirname(__filename), '..', 'helpers', 'custom-logic-worker.js'), { + workerData: { + execFunc: `const [done, user, documents, mathjs, artifacts] = arguments;${execCode}${ref.options.expression}`, + user, + documents, + artifacts + }, + }); + worker.on('error', (error) => { + reject(error); + // worker.terminate() + }); + worker.on('message', (result) => { + done(result); + console.log(result); + // worker.terminate() + }); + // worker.on('exit', (code) => { + // console.log('Worker exit with code', code); + // }); }); } diff --git a/guardian-service/src/policy-engine/blocks/http-request-block.ts b/guardian-service/src/policy-engine/blocks/http-request-block.ts new file mode 100644 index 0000000000..a53efe7c65 --- /dev/null +++ b/guardian-service/src/policy-engine/blocks/http-request-block.ts @@ -0,0 +1,203 @@ +import { BasicBlock } from '@policy-engine/helpers/decorators/basic-block'; +import { ChildrenType, ControlType } from '@policy-engine/interfaces/block-about'; +import { IPolicyEvent, PolicyInputEventType, PolicyOutputEventType } from '@policy-engine/interfaces'; +import { ActionCallback } from '@policy-engine/helpers/decorators'; +import { CatchErrors } from '@policy-engine/helpers/decorators/catch-errors'; +import { + IPolicyBlock, + IPolicyCalculateBlock, + IPolicyDocument, + IPolicyEventState +} from '@policy-engine/policy-engine.interface'; +import { PolicyComponentsUtils } from '@policy-engine/policy-components-utils'; +import { ExternalDocuments, ExternalEvent, ExternalEventType } from '@policy-engine/interfaces/external-event'; +import { PolicyUtils } from '@policy-engine/helpers/utils'; +import { Workers } from '@helpers/workers'; +import { WorkerTaskType } from '@guardian/interfaces'; +import { VcHelper } from '@helpers/vc-helper'; +import { VcDocument } from '@hedera-modules'; +import { PolicyValidationResultsContainer } from '@policy-engine/policy-validation-results-container'; + +/** + * Http request block + */ +@BasicBlock({ + blockType: 'httpRequestBlock', + commonBlock: false, + about: { + label: 'Request data', + title: `Add 'Request Data' Block`, + post: false, + get: false, + children: ChildrenType.None, + control: ControlType.Server, + input: [ + PolicyInputEventType.RunEvent + ], + output: [ + PolicyOutputEventType.RunEvent, + PolicyOutputEventType.RefreshEvent, + PolicyOutputEventType.ErrorEvent + ], + defaultEvent: true + } +}) +export class HttpRequestBlock { + + /** + * Get object property by path + * @param obj + * @param fieldPath + * @private + */ + private getFieldByPath(obj: any, fieldPath: string): string { + const fieldPathArray = fieldPath.split('.'); + + let currentValue = obj; + + let currentField = fieldPathArray.shift(); + while (currentField) { + if (currentValue === undefined) { + currentValue = ''; + break; + } + currentValue = currentValue[currentField]; + currentField = fieldPathArray.shift(); + } + + return currentValue; + } + + /** + * Replace variables to values in string + * @param input + * @param variablesObj + * @private + */ + private replaceVariablesInString(input: string, variablesObj: any): string { + let result = input; + const regExp = /\$\{.+?\}/gm; + let variableItem = regExp.exec(input); + while (variableItem !== null) { + const variable = variableItem[0]; + const varPath = variable.substr(2, variable.length - 3); + + const variableValue = this.getFieldByPath(variablesObj, varPath); + result = result.replace(variable, variableValue) + + variableItem = regExp.exec(input); + } + return result; + } + + /** + * Request document + * @param method + * @param url + * @param headers + * @param body + */ + async requestDocument(method, url, headers, body): Promise { + const ref = PolicyComponentsUtils.GetBlockRef(this); + + const res = await new Workers().addNonRetryableTask({ + type: WorkerTaskType.HTTP_REQUEST, + data: { + payload: { method, url, headers, body } + } + }, 10); + if (!res) { + throw new Error('Invalid response'); + } + + let verify: boolean; + try { + const VCHelper = new VcHelper(); + const result = await VCHelper.verifySchema(res); + verify = result.ok; + if (verify) { + verify = await VCHelper.verifyVC(res); + } + } catch (error) { + ref.error(`Verify VC: ${PolicyUtils.getErrorMessage(error)}`) + verify = false; + } + + if (!verify) { + throw new Error('Document is not VC'); + } + + return VcDocument.fromJsonTree(res); + } + + /** + * Action callback + * @event PolicyEventType.Run + * @param {IPolicyEvent} event + */ + @ActionCallback({ + output: [ + PolicyOutputEventType.RunEvent, + PolicyOutputEventType.RefreshEvent, + PolicyOutputEventType.ErrorEvent + ] + }) + @CatchErrors() + public async runAction(event: IPolicyEvent) { + const ref = PolicyComponentsUtils.GetBlockRef(this); + event.data.data = event.data.data || {}; + + const variablesObj: any = { + did: event?.user?.did, + username: event?.user.username + } + + let inputObject; + if (Array.isArray(event.data?.data)) { + variablesObj.documents = inputObject = (event?.data?.data as IPolicyDocument[])?.map(i => i.document); + } else { + variablesObj.document = inputObject = (event?.data?.data as IPolicyDocument)?.document; + } + + try { + const method = ref.options.method; + const url = this.replaceVariablesInString(ref.options.url, variablesObj); + const headers = {}; + if (Array.isArray(ref.options.headers)) { + for (const header of ref.options.headers) { + headers[header.name] = this.replaceVariablesInString(header.value, variablesObj) + } + } + const requestBody = this.replaceVariablesInString(JSON.stringify(inputObject), variablesObj); + + const doc = await this.requestDocument(method, url, headers, JSON.parse(requestBody)); + const item = PolicyUtils.createVC(ref, event.user, doc); + + ref.triggerEvents(PolicyOutputEventType.RunEvent, event.user, {data: item}); + ref.triggerEvents(PolicyOutputEventType.ReleaseEvent, event.user, null); + ref.triggerEvents(PolicyOutputEventType.RefreshEvent, event.user, {data: item}); + PolicyComponentsUtils.ExternalEventFn(new ExternalEvent(ExternalEventType.Run, ref, event?.user, { + documents: ExternalDocuments({data: item}) + })); + } catch (error) { + ref.error(PolicyUtils.getErrorMessage(error)); + } + } + + /** + * Validate block data + * @param resultsContainer + */ + public async validate(resultsContainer: PolicyValidationResultsContainer): Promise { + const ref = PolicyComponentsUtils.GetBlockRef(this); + + if (!ref.options.url?.trim()) { + resultsContainer.addBlockError(ref.uuid, 'Option "url" must be set'); + } + + if (!['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].find(item => item === ref.options.method?.toUpperCase())) { + resultsContainer.addBlockError(ref.uuid, `Option "method" must be "GET", "POST", "PUT", "PATCH" or "DELETE"`); + } + } + +} diff --git a/guardian-service/src/policy-engine/blocks/impact-addon.ts b/guardian-service/src/policy-engine/blocks/impact-addon.ts new file mode 100644 index 0000000000..62f130fdf2 --- /dev/null +++ b/guardian-service/src/policy-engine/blocks/impact-addon.ts @@ -0,0 +1,135 @@ +import { TokenAddon } from '@policy-engine/helpers/decorators'; +import { PolicyValidationResultsContainer } from '@policy-engine/policy-validation-results-container'; +import { PolicyComponentsUtils } from '@policy-engine/policy-components-utils'; +import { AnyBlockType, IPolicyCalculateAddon } from '@policy-engine/policy-engine.interface'; +import { ChildrenType, ControlType, PropertyType } from '@policy-engine/interfaces/block-about'; +import { IHederaAccount, PolicyUtils } from '@policy-engine/helpers/utils'; +import { IPolicyUser } from '@policy-engine/policy-user'; +import { Schema, SchemaEntity, SchemaHelper } from '@guardian/interfaces'; +import { VcDocument } from '@hedera-modules'; +import { VcHelper } from '@helpers/vc-helper'; +import { BlockActionError } from '@policy-engine/errors'; +import { PropertyValidator } from '@policy-engine/helpers/property-validator'; + +/** + * Calculate math addon + */ +@TokenAddon({ + blockType: 'impactAddon', + commonBlock: true, + about: { + label: 'Impact', + title: `Add 'Impact'`, + post: false, + get: false, + children: ChildrenType.None, + control: ControlType.Special, + input: null, + output: null, + defaultEvent: false, + properties: [{ + name: 'impactType', + label: 'Impact type', + title: 'Impact type', + type: PropertyType.Select, + items: [{ + label: 'Primary Impacts', + value: 'Primary Impacts' + }, { + label: 'Secondary Impacts', + value: 'Secondary Impacts' + }], + default: 'Secondary Impacts', + required: true + }, { + name: 'label', + label: 'Label', + title: 'Label', + type: PropertyType.Input + }, { + name: 'description', + label: 'Description', + title: 'Description', + type: PropertyType.Input + }, { + name: 'amount', + label: 'Amount (Formula)', + title: 'Amount (Formula)', + required: true, + type: PropertyType.Input + }, { + name: 'unit', + label: 'Unit', + title: 'Unit', + type: PropertyType.Input + }] + } +}) +export class TokenOperationAddon { + /** + * Schema + * @private + */ + private schema: any | null; + + /** + * Get Schema + */ + async getSchema(): Promise { + if (!this.schema) { + const ref = PolicyComponentsUtils.GetBlockRef(this); + this.schema = await ref.databaseServer.getSchemaByType(ref.topicId, SchemaEntity.ACTIVITY_IMPACT); + if (!this.schema) { + throw new BlockActionError('Waiting for schema', ref.blockType, ref.uuid); + } + } + return this.schema; + } + + /** + * Run logic + * @param documents + * @param user + */ + public async run(documents: VcDocument[], root: IHederaAccount, user: IPolicyUser): Promise { + const ref = PolicyComponentsUtils.GetBlockRef(this); + const policySchema = await this.getSchema(); + const amount = PolicyUtils.aggregate(ref.options.amount, documents); + const vcHelper = new VcHelper(); + const vcSubject: any = { + ...SchemaHelper.getContext(policySchema), + impactType: ref.options.impactType === 'Primary Impacts' ? 'Primary Impacts' : 'Secondary Impacts', + date: (new Date()).toISOString(), + amount: amount.toString(), + } + if (ref.options.unit) { + vcSubject.unit = ref.options.unit; + } + if (ref.options.label) { + vcSubject.label = ref.options.label; + } + if (ref.options.description) { + vcSubject.description = ref.options.description; + } + const vc = await vcHelper.createVC(root.did, root.hederaAccountKey, vcSubject); + return vc; + } + + /** + * Validate block options + * @param resultsContainer + */ + public async validate(resultsContainer: PolicyValidationResultsContainer): Promise { + const ref = PolicyComponentsUtils.GetBlockRef(this); + try { + resultsContainer.checkBlockError(ref.uuid, + PropertyValidator.inputValidator('amount', ref.options.amount, 'string') + ); + resultsContainer.checkBlockError(ref.uuid, + PropertyValidator.selectValidator('impactType', ref.options.impactType, ['Primary Impacts', 'Secondary Impacts']) + ); + } catch (error) { + resultsContainer.addBlockError(ref.uuid, `Unhandled exception ${PolicyUtils.getErrorMessage(error)}`); + } + } +} diff --git a/guardian-service/src/policy-engine/blocks/index.ts b/guardian-service/src/policy-engine/blocks/index.ts index bc654744ad..556d1e92aa 100644 --- a/guardian-service/src/policy-engine/blocks/index.ts +++ b/guardian-service/src/policy-engine/blocks/index.ts @@ -34,3 +34,5 @@ export { MultiSignBlock } from './multi-sign-block'; export { CalculateMathVariables } from './calculate-math-variables'; export { CreateTokenBlock } from './create-token-block'; export { SplitBlock } from './split-block'; +export { TokenOperationAddon } from './impact-addon'; +export { HttpRequestBlock } from './http-request-block'; diff --git a/guardian-service/src/policy-engine/blocks/mint-block.ts b/guardian-service/src/policy-engine/blocks/mint-block.ts index f53c0d4320..f3d0172749 100644 --- a/guardian-service/src/policy-engine/blocks/mint-block.ts +++ b/guardian-service/src/policy-engine/blocks/mint-block.ts @@ -1,4 +1,4 @@ -import { ActionCallback, BasicBlock } from '@policy-engine/helpers/decorators'; +import { ActionCallback, TokenBlock } from '@policy-engine/helpers/decorators'; import { BlockActionError } from '@policy-engine/errors'; import { DocumentSignature, GenerateUUIDv4, SchemaEntity, SchemaHelper } from '@guardian/interfaces'; import { PolicyValidationResultsContainer } from '@policy-engine/policy-validation-results-container'; @@ -8,7 +8,7 @@ import { VcDocument, VCMessage, MessageAction, MessageServer, VPMessage, Message import { VcHelper } from '@helpers/vc-helper'; import { Token as TokenCollection } from '@entity/token'; import { DataTypes, IHederaAccount, PolicyUtils } from '@policy-engine/helpers/utils'; -import { AnyBlockType, IPolicyDocument, IPolicyEventState } from '@policy-engine/policy-engine.interface'; +import { AnyBlockType, IPolicyDocument, IPolicyMintEventState, IPolicyTokenBlock } from '@policy-engine/policy-engine.interface'; import { IPolicyEvent, PolicyInputEventType, PolicyOutputEventType } from '@policy-engine/interfaces'; import { ChildrenType, ControlType } from '@policy-engine/interfaces/block-about'; import { IPolicyUser } from '@policy-engine/policy-user'; @@ -18,7 +18,7 @@ import { MintService } from '@policy-engine/multi-policy-service/mint-service'; /** * Mint block */ -@BasicBlock({ +@TokenBlock({ blockType: 'mintDocumentBlock', commonBlock: true, about: { @@ -29,7 +29,8 @@ import { MintService } from '@policy-engine/multi-policy-service/mint-service'; children: ChildrenType.None, control: ControlType.Server, input: [ - PolicyInputEventType.RunEvent + PolicyInputEventType.RunEvent, + PolicyInputEventType.AdditionalMintEvent ], output: [ PolicyOutputEventType.RunEvent, @@ -40,6 +41,105 @@ import { MintService } from '@policy-engine/multi-policy-service/mint-service'; } }) export class MintBlock { + /** + * Get Token + * @param ref + * @param docs + * @private + */ + private async getToken(ref: AnyBlockType, docs: IPolicyDocument[]): Promise { + let token: TokenCollection; + if (ref.options.useTemplate) { + if (docs[0].tokens) { + const tokenId = docs[0].tokens[ref.options.template]; + token = await ref.databaseServer.getTokenById(tokenId, ref.dryRun); + } + } else { + token = await ref.databaseServer.getTokenById(ref.options.tokenId); + } + if (!token) { + throw new BlockActionError('Bad token id', ref.blockType, ref.uuid); + } + return token; + } + + /** + * Get Objects + * @param ref + * @param docs + * @private + */ + private getObjects(ref: AnyBlockType, docs: IPolicyDocument[]): any { + const vcs: VcDocument[] = []; + const messages: string[] = []; + const topics: string[] = []; + const field = ref.options.accountId || 'default'; + const accounts: string[] = []; + for (const doc of docs) { + if (doc.signature === DocumentSignature.INVALID) { + throw new BlockActionError('Invalid VC proof', ref.blockType, ref.uuid); + } + const json = VcDocument.fromJsonTree(doc.document); + vcs.push(json); + if (doc.messageId) { + messages.push(doc.messageId); + } + if (doc.topicId) { + topics.push(doc.topicId); + } + if (doc.accounts) { + const accountId: string = doc.accounts[field]; + accounts.push(accountId); + } + } + return { vcs, messages, topics, accounts } + } + + /** + * Get Additional Messages + * @param additionalDocs + * @private + */ + private getAdditionalMessages(additionalDocs: IPolicyDocument[]): string[] { + const additionalMessages: string[] = []; + if (additionalDocs) { + for (const doc of additionalDocs) { + if (doc.messageId) { + additionalMessages.push(doc.messageId); + } + } + } + return additionalMessages; + } + + /** + * Get Account + * @param ref + * @param docs + * @param accounts + * @private + */ + private async getAccount(ref: AnyBlockType, docs: IPolicyDocument[], accounts: string[]): Promise { + let targetAccountId: string; + if (ref.options.accountType !== 'custom-value') { + const firstAccounts = accounts[0]; + if (accounts.find(a => a !== firstAccounts)) { + ref.error(`More than one account found! Transfer made on the first (${firstAccounts})`); + } + if (ref.options.accountId) { + targetAccountId = firstAccounts; + } else { + targetAccountId = await PolicyUtils.getHederaAccountId(ref, docs[0].owner); + } + if (!targetAccountId) { + throw new BlockActionError('Token recipient not set', ref.blockType, ref.uuid); + } + } else { + targetAccountId = ref.options.accountIdValue; + } + return targetAccountId; + } + /** * Create mint VC * @param root @@ -64,6 +164,51 @@ export class MintBlock { return mintVC; } + /** + * Create report VC + * @param ref + * @param root + * @param user + * @param documents + * @param messages + * @param additionalMessages + * @private + */ + private async createReportVC( + ref: IPolicyTokenBlock, + root: IHederaAccount, + user: IPolicyUser, + documents: VcDocument[], + messages: string[], + additionalMessages: string[], + ): Promise { + const addons = ref.getAddons(); + const result: VcDocument[] = []; + if ( + (addons && addons.length) || + (additionalMessages && additionalMessages.length) + ) { + const vcHelper = new VcHelper(); + const policySchema = await ref.databaseServer.getSchemaByType(ref.topicId, SchemaEntity.TOKEN_DATA_SOURCE); + const vcSubject: any = { ...SchemaHelper.getContext(policySchema) }; + if (messages) { + vcSubject.dataSource = messages.slice(); + } + if (additionalMessages) { + vcSubject.relationships = additionalMessages.slice(); + } + const vc = await vcHelper.createVC(root.did, root.hederaAccountKey, vcSubject); + result.push(vc); + } + if (addons && addons.length) { + for (const addon of addons) { + const impact = await addon.run(documents, root, user); + result.push(impact); + } + } + return result; + } + /** * Create VP * @param root @@ -85,62 +230,71 @@ export class MintBlock { /** * Mint processing * @param token - * @param documents - * @param relationships * @param topicId - * @param root * @param user + * @param accountId + * @param documents + * @param messages + * @param additionalMessages * @private */ private async mintProcessing( token: TokenCollection, - documents: VcDocument[], - relationships: string[], topicId: string, - root: IHederaAccount, user: IPolicyUser, - targetAccountId: string + accountId: string, + documents: VcDocument[], + messages: string[], + additionalMessages: string[], ): Promise<[IPolicyDocument, number]> { - const ref = PolicyComponentsUtils.GetBlockRef(this); + const ref = PolicyComponentsUtils.GetBlockRef(this); const uuid = GenerateUUIDv4(); const amount = PolicyUtils.aggregate(ref.options.rule, documents); - if (Number.isNaN(amount) || !Number.isFinite(amount)) { throw new BlockActionError(`Invalid token value: ${amount}`, ref.blockType, ref.uuid); } - const [tokenValue, tokenAmount] = PolicyUtils.tokenAmount(token, amount); + + const root = await PolicyUtils.getHederaAccount(ref, ref.policyOwner); const mintVC = await this.createMintVC(root, token, tokenAmount, ref); - const vcs = documents.slice(); - vcs.push(mintVC); - const vp = await this.createVP(root, uuid, vcs); + const reportVC = await this.createReportVC(ref, root, user, documents, messages, additionalMessages); + let vp: any; + if (reportVC && reportVC.length) { + const vcs = [...reportVC, mintVC]; + vp = await this.createVP(root, uuid, vcs); + } else { + const vcs = [...documents, mintVC]; + vp = await this.createVP(root, uuid, vcs); + } + + ref.log(`Topic Id: ${topicId}`); const messageServer = new MessageServer(root.hederaAccountId, root.hederaAccountKey, ref.dryRun); - ref.log(`Topic Id: ${topicId}`); + // #region Save Mint VC const topic = await PolicyUtils.getPolicyTopic(ref, topicId); const vcMessage = new VCMessage(MessageAction.CreateVC); vcMessage.setDocument(mintVC); - vcMessage.setRelationships(relationships); + vcMessage.setRelationships(messages); const vcMessageResult = await messageServer .setTopicObject(topic) .sendMessage(vcMessage); + const mintVcDocument = PolicyUtils.createVC(ref, user, mintVC); + mintVcDocument.type = DataTypes.MINT; + mintVcDocument.schema = `#${mintVC.getSubjectType()}`; + mintVcDocument.messageId = vcMessageResult.getId(); + mintVcDocument.topicId = vcMessageResult.getTopicId(); + mintVcDocument.relationships = messages; + await ref.databaseServer.saveVC(mintVcDocument); + // #endregion - const vcDocument = PolicyUtils.createVC(ref, user, mintVC); - vcDocument.type = DataTypes.MINT; - vcDocument.schema = `#${mintVC.getSubjectType()}`; - vcDocument.messageId = vcMessageResult.getId(); - vcDocument.topicId = vcMessageResult.getTopicId(); - vcDocument.relationships = relationships; - - await ref.databaseServer.saveVC(vcDocument); + messages.push(vcMessageResult.getId()); - relationships.push(vcMessageResult.getId()); + // #region Save Mint VP const vpMessage = new VPMessage(MessageAction.CreateVP); vpMessage.setDocument(vp); - vpMessage.setRelationships(relationships); - + vpMessage.setRelationships(messages); const vpMessageResult = await messageServer .setTopicObject(topic) .sendMessage(vpMessage); @@ -149,25 +303,43 @@ export class MintBlock { vpDocument.type = DataTypes.MINT; vpDocument.messageId = vpMessageId; vpDocument.topicId = vpMessageResult.getTopicId(); - const savedVp = await ref.databaseServer.saveVP(vpDocument); - const transactionMemo = `${vpMessageId} ${MessageMemo.parseMemo(true, ref.options.memo, savedVp)}`.trimEnd(); + // #endregion + const transactionMemo = `${vpMessageId} ${MessageMemo.parseMemo(true, ref.options.memo, savedVp)}`.trimEnd(); await MintService.mint( - ref, - token, - tokenValue, - user, - root, - targetAccountId, - vpMessageId, - transactionMemo, - documents + ref, token, tokenValue, user, root, accountId, + vpMessageId, transactionMemo, documents ); - return [savedVp, tokenValue]; } + /** + * Run action + * @event PolicyEventType.AdditionalMintEvent + * @param {IPolicyEvent} event + */ + @ActionCallback({ + type: PolicyInputEventType.AdditionalMintEvent + }) + async additionalMintEvent(event: IPolicyEvent) { + const ref = PolicyComponentsUtils.GetBlockRef(this); + + const docs = PolicyUtils.getArray(event.data.data); + if (!docs.length && docs[0]) { + throw new BlockActionError('Bad VC', ref.blockType, ref.uuid); + } + + const docOwner = PolicyUtils.getDocumentOwner(ref, docs[0]); + if (!docOwner) { + throw new BlockActionError('Bad User DID', ref.blockType, ref.uuid); + } + + const additionalDocs = PolicyUtils.getArray(event.data.result); + + await this.run(ref, event, docOwner, docs, additionalDocs); + } + /** * Run action * @event PolicyEventType.Run @@ -181,8 +353,8 @@ export class MintBlock { ] }) @CatchErrors() - async runAction(event: IPolicyEvent) { - const ref = PolicyComponentsUtils.GetBlockRef(this); + async runAction(event: IPolicyEvent) { + const ref = PolicyComponentsUtils.GetBlockRef(this); const docs = PolicyUtils.getArray(event.data.data); if (!docs.length && docs[0]) { @@ -194,81 +366,39 @@ export class MintBlock { throw new BlockActionError('Bad User DID', ref.blockType, ref.uuid); } - let token; - if (ref.options.useTemplate) { - if (docs[0].tokens) { - const tokenId = docs[0].tokens[ref.options.template]; - token = await ref.databaseServer.getTokenById(tokenId, ref.dryRun); - } - } else { - token = await ref.databaseServer.getTokenById(ref.options.tokenId); - } - - if (!token) { - throw new BlockActionError('Bad token id', ref.blockType, ref.uuid); - } - - const vcs: VcDocument[] = []; - const vsMessages: string[] = []; - const topicIds: string[] = []; - const field = ref.options.accountId || 'default'; - const accounts: string[] = []; - for (const doc of docs) { - if (doc.signature === DocumentSignature.INVALID) { - throw new BlockActionError('Invalid VC proof', ref.blockType, ref.uuid); - } - const json = VcDocument.fromJsonTree(doc.document); - - vcs.push(json); - if (doc.messageId) { - vsMessages.push(doc.messageId); - } - if (doc.topicId) { - topicIds.push(doc.topicId); - } - if (doc.accounts) { - const accountId: string = doc.accounts[field]; - accounts.push(accountId); - } - } + await this.run(ref, event, docOwner, docs); + } - const topicId = topicIds[0]; - let targetAccountId: string; - if (ref.options.accountType !== 'custom-value') { - const firstAccounts = accounts[0]; - if (accounts.find(a => a !== firstAccounts)) { - ref.error(`More than one account found! Transfer made on the first (${firstAccounts})`); - } - if (ref.options.accountId) { - targetAccountId = firstAccounts; - } else { - targetAccountId = await PolicyUtils.getHederaAccountId(ref, docs[0].owner); - } - if (!targetAccountId) { - throw new BlockActionError('Token recipient not set', ref.blockType, ref.uuid); - } - } else { - targetAccountId = ref.options.accountIdValue; - } + /** + * Run action + * @param {IPolicyTokenBlock} ref + * @param {IPolicyEvent} event + * @param {IPolicyDocument[]} docs + * @param {IPolicyDocument[]} additionalDocs + */ + private async run( + ref: IPolicyTokenBlock, + event: IPolicyEvent, + user: IPolicyUser, + docs: IPolicyDocument[], + additionalDocs?: IPolicyDocument[] + ) { + const token = await this.getToken(ref, docs); + const { vcs, messages, topics, accounts } = this.getObjects(ref, docs); + const additionalMessages = this.getAdditionalMessages(additionalDocs); + const topicId = topics[0]; + const accountId = await this.getAccount(ref, docs, accounts); + const [vp, amount] = await this.mintProcessing(token, topicId, user, accountId, vcs, messages, additionalMessages); - const root = await PolicyUtils.getHederaAccount(ref, ref.policyOwner); + event.data.result = vp; + ref.triggerEvents(PolicyOutputEventType.RunEvent, user, event.data); + ref.triggerEvents(PolicyOutputEventType.ReleaseEvent, user, null); + ref.triggerEvents(PolicyOutputEventType.RefreshEvent, user, event.data); - const [vp, tokenValue] = await this.mintProcessing( - token, - vcs, - vsMessages, - topicId, - root, - docOwner, - targetAccountId - ); - ref.triggerEvents(PolicyOutputEventType.RunEvent, docOwner, event.data); - ref.triggerEvents(PolicyOutputEventType.ReleaseEvent, docOwner, null); - ref.triggerEvents(PolicyOutputEventType.RefreshEvent, docOwner, event.data); - PolicyComponentsUtils.ExternalEventFn(new ExternalEvent(ExternalEventType.Run, ref, docOwner, { + PolicyComponentsUtils.ExternalEventFn(new ExternalEvent(ExternalEventType.Run, ref, user, { tokenId: token.tokenId, - accountId: targetAccountId, - amount: tokenValue, + accountId, + amount, documents: ExternalDocuments(docs), result: ExternalDocuments(vp), })); diff --git a/guardian-service/src/policy-engine/blocks/report-block.ts b/guardian-service/src/policy-engine/blocks/report-block.ts index 4585d8d93a..945e9ca532 100644 --- a/guardian-service/src/policy-engine/blocks/report-block.ts +++ b/guardian-service/src/policy-engine/blocks/report-block.ts @@ -3,12 +3,11 @@ import { Report } from '@policy-engine/helpers/decorators'; import { PolicyComponentsUtils } from '@policy-engine/policy-components-utils'; import { IPolicyReportBlock } from '@policy-engine/policy-engine.interface'; import { + IImpactReport, IPolicyReport, IReport, IReportItem, - ITokenReport, IVCReport, - IVPReport, SchemaEntity, } from '@guardian/interfaces'; import { BlockActionError } from '@policy-engine/errors'; @@ -17,6 +16,8 @@ import { PolicyInputEventType } from '@policy-engine/interfaces'; import { IPolicyUser } from '@policy-engine/policy-user'; import { PolicyUtils } from '@policy-engine/helpers/utils'; import { ExternalEvent, ExternalEventType } from '@policy-engine/interfaces/external-event'; +import { VpDocument } from '@entity/vp-document'; +import { VcDocument } from '@entity/vc-document'; /** * Report block @@ -92,11 +93,212 @@ export class ReportBlock { } } + /** + * Add report item by VP + * @param report + * @param variables + * @param vp + */ + private async addReportByVP( + report: IReport, + variables: any, + vp: VpDocument, + isMain: boolean = false + ): Promise { + const vcs = vp.document.verifiableCredential || []; + const mintIndex = Math.max(1, vcs.length - 1); + const mint = vcs[mintIndex]; + report.vpDocument = { + type: 'VP', + title: 'Verified Presentation', + tag: vp.tag, + hash: vp.hash, + issuer: vp.owner, + username: vp.owner, + document: vp + } + report.mintDocument = { + type: 'VC', + tokenId: getVCField(mint, 'tokenId'), + date: getVCField(mint, 'date'), + amount: getVCField(mint, 'amount'), + tag: vp.tag, + issuer: vp.owner, + username: vp.owner, + document: { + owner: null, + hash: null, + type: null, + policyId: null, + tag: null, + option: null, + document: mint + } + } + variables.actionId = mint.id; + variables.actionSubjectId = mint.credentialSubject[0].id; + + report = await this.addReportByVCs(report, variables, vcs, vp); + if (isMain) { + report = await this.searchAdditionalDocuments(report, vcs, vp); + } + + return report; + } + + /** + * Add report item by VCs + * @param report + * @param variables + * @param vcs + * @param vp + */ + private async addReportByVCs( + report: IReport, + variables: any, + vcs: any[], + vp: VpDocument + ): Promise { + const ref = PolicyComponentsUtils.GetBlockRef(this); + + const dataSource: any[] = []; + const impacts: IImpactReport[] = []; + const documentIds: string[] = []; + const documentSubjectIds: string[] = []; + for (let i = 0; i < vcs.length - 1; i++) { + const doc = vcs[i]; + const credentialSubject = doc.credentialSubject[0]; + if (credentialSubject.type === 'TokenDataSource') { + dataSource.push(doc); + } else if (credentialSubject.type === 'ActivityImpact') { + impacts.push({ + type: 'VC', + impactType: getVCField(doc, 'impactType'), + label: getVCField(doc, 'label'), + description: getVCField(doc, 'description'), + amount: getVCField(doc, 'amount'), + unit: getVCField(doc, 'unit'), + date: getVCField(doc, 'date'), + tag: vp.tag, + issuer: vp.owner, + username: vp.owner, + document: { + owner: null, + hash: null, + type: null, + policyId: null, + tag: null, + option: null, + document: doc + } + }); + } else { + documentIds.push(doc.id); + documentSubjectIds.push(credentialSubject.id); + } + } + if (dataSource.length) { + const messageIds = []; + for (const item of dataSource) { + const ids = item.credentialSubject[0].dataSource; + if (Array.isArray(ids)) { + for (const id of ids) { + messageIds.push(id); + } + } else { + messageIds.push(ids); + } + } + const items = await ref.databaseServer.getVcDocuments({ + where: { messageId: { $in: messageIds } } + }); + for (const item of items) { + documentIds.push(item.document.id); + documentSubjectIds.push(item.document.credentialSubject[0].id); + } + } + if (impacts.length) { + report.impacts = impacts; + } + variables.documentId = documentIds[0]; + variables.documentSubjectId = documentSubjectIds[0]; + variables.documentIds = documentIds; + variables.documentSubjectIds = documentSubjectIds; + return report; + } + + /** + * Add report item by VC + * @param report + * @param variables + * @param vcs + * @param vp + */ + private async addReportByVC(report: IReport, variables: any, vc: VcDocument): Promise { + const vcDocument: IVCReport = { + type: 'VC', + title: 'Verifiable Credential', + tag: vc.tag, + hash: vc.hash, + issuer: vc.owner, + username: vc.owner, + document: vc + } + report.vcDocument = vcDocument; + variables.documentId = vc.document.id; + variables.documentSubjectId = vc.document.credentialSubject[0].id; + + return report; + } + + /** + * Add report item by Policy + * @param report + * @param variables + * @param policy + */ + private async addReportByPolicy(report: IReport, variables: any, policy: VcDocument): Promise { + const ref = PolicyComponentsUtils.GetBlockRef(this); + + const policyDocument: IPolicyReport = { + type: 'VC', + name: getVCField(policy.document, 'name'), + description: getVCField(policy.document, 'description'), + version: getVCField(policy.document, 'version'), + tag: 'Policy Created', + issuer: policy.owner, + username: policy.owner, + document: policy + } + report.policyDocument = policyDocument; + + const policyCreator = await ref.databaseServer.getVcDocument({ + type: SchemaEntity.POLICY, + owner: policy.owner + }); + + if (policyCreator) { + const policyCreatorDocument: IReportItem = { + type: 'VC', + title: 'StandardRegistry', + description: 'Account Creation', + visible: true, + tag: 'Account Creation', + issuer: policy.owner, + username: policy.owner, + document: policyCreator + } + report.policyCreatorDocument = policyCreatorDocument; + } + + return report; + } + /** * Report user map * @param report */ - async reportUserMap(report: IReport) { + private async reportUserMap(report: IReport): Promise { const map: any = {}; if (report.vpDocument) { report.vpDocument.username = await this.getUserName(report.vpDocument.username, map); @@ -114,6 +316,66 @@ export class ReportBlock { report.policyCreatorDocument.username = await this.getUserName(report.policyCreatorDocument.username, map); } await this.itemUserMap(report.documents, map); + + return report + } + + /** + * Search Additional Documents + * @param report + * @param vcs + * @param vp + */ + private async searchAdditionalDocuments( + report: IReport, + vcs: any[], + vp: VpDocument + ): Promise { + const ref = PolicyComponentsUtils.GetBlockRef(this); + const messageIds = []; + for (let i = 0; i < vcs.length - 1; i++) { + const doc = vcs[i]; + const credentialSubject = doc.credentialSubject[0]; + if (credentialSubject.type === 'TokenDataSource' && credentialSubject.relationships) { + if (Array.isArray(credentialSubject.relationships)) { + for (const relationship of credentialSubject.relationships) { + messageIds.push(relationship); + } + } else { + messageIds.push(credentialSubject.relationships); + } + } + } + const additionalReports = []; + if (messageIds.length) { + const additionalVps = await ref.databaseServer.getVpDocuments({ + where: { + messageId: { $in: messageIds }, + policyId: { $eq: ref.policyId } + } + }); + for (const additionalVp of additionalVps) { + const additionalReport = await this.addReportByVP({}, {}, additionalVp); + additionalReports.push(additionalReport); + } + } + if (vp.messageId) { + const additionalVps = await ref.databaseServer.getVpDocuments({ + where: { + 'document.verifiableCredential.credentialSubject.type': { $eq: 'TokenDataSource' }, + 'document.verifiableCredential.credentialSubject.relationships': { $eq: vp.messageId }, + 'policyId': { $eq: ref.policyId } + } + }); + for (const additionalVp of additionalVps) { + const additionalReport = await this.addReportByVP({}, {}, additionalVp); + additionalReports.push(additionalReport); + } + } + if (additionalReports.length) { + report.additionalDocuments = additionalReports; + } + return report; } /** @@ -141,9 +403,10 @@ export class ReportBlock { owner: user.did }; - const report: IReport = { + let report: IReport = { vpDocument: null, vcDocument: null, + impacts: null, mintDocument: null, policyDocument: null, policyCreatorDocument: null, @@ -151,69 +414,12 @@ export class ReportBlock { } const vp = await ref.databaseServer.getVpDocument({ hash, policyId: ref.policyId }); - if (vp) { - const vpDocument: IVPReport = { - type: 'VP', - title: 'Verified Presentation', - tag: vp.tag, - hash: vp.hash, - issuer: vp.owner, - username: vp.owner, - document: vp - } - report.vpDocument = vpDocument; - - const mintIndex = Math.max(1, vp.document.verifiableCredential.length - 1); - const mint = vp.document.verifiableCredential[mintIndex]; - const mintDocument: ITokenReport = { - type: 'VC', - tokenId: getVCField(mint, 'tokenId'), - date: getVCField(mint, 'date'), - tag: vp.tag, - issuer: vp.owner, - username: vp.owner, - document: { - owner: null, - hash: null, - type: null, - policyId: null, - tag: null, - option: null, - document: mint - } - } - report.mintDocument = mintDocument; - variables.actionId = mint.id; - variables.actionSubjectId = mint.credentialSubject[0].id; - - const documentIds = []; - const documentSubjectIds = []; - for (let i = 0; i < vp.document.verifiableCredential.length - 1; i++) { - const doc = vp.document.verifiableCredential[i]; - documentIds.push(doc.id); - documentSubjectIds.push(doc.credentialSubject[0].id); - } - variables.documentId = documentIds[0]; - variables.documentSubjectId = documentSubjectIds[0]; - variables.documentIds = documentIds; - variables.documentSubjectIds = documentSubjectIds; + report = await this.addReportByVP(report, variables, vp, true); } else { const vc = await ref.databaseServer.getVcDocument({ hash, policyId: ref.policyId }) - if (vc) { - const vcDocument: IVCReport = { - type: 'VC', - title: 'Verifiable Credential', - tag: vc.tag, - hash: vc.hash, - issuer: vc.owner, - username: vc.owner, - document: vc - } - report.vcDocument = vcDocument; - variables.documentId = vc.document.id; - variables.documentSubjectId = vc.document.credentialSubject[0].id; + report = await this.addReportByVC(report, variables, vc); } } @@ -223,36 +429,7 @@ export class ReportBlock { }); if (policy) { - const policyDocument: IPolicyReport = { - type: 'VC', - name: getVCField(policy.document, 'name'), - description: getVCField(policy.document, 'description'), - version: getVCField(policy.document, 'version'), - tag: 'Policy Created', - issuer: policy.owner, - username: policy.owner, - document: policy - } - report.policyDocument = policyDocument; - - const policyCreator = await ref.databaseServer.getVcDocument({ - type: SchemaEntity.POLICY, - owner: policy.owner - }); - - if (policyCreator) { - const policyCreatorDocument: IReportItem = { - type: 'VC', - title: 'StandardRegistry', - description: 'Account Creation', - visible: true, - tag: 'Account Creation', - issuer: policy.owner, - username: policy.owner, - document: policyCreator - } - report.policyCreatorDocument = policyCreatorDocument; - } + report = await this.addReportByPolicy(report, variables, policy); } const reportItems = ref.getItems(); @@ -260,7 +437,12 @@ export class ReportBlock { await reportItem.run(documents, variables); } - await this.reportUserMap(report); + report = await this.reportUserMap(report); + if (report.additionalDocuments) { + for (let i = 0; i < report.additionalDocuments.length; i++) { + report.additionalDocuments[i] = await this.reportUserMap(report.additionalDocuments[i]); + } + } return { hash, diff --git a/guardian-service/src/policy-engine/helpers/custom-logic-worker.ts b/guardian-service/src/policy-engine/helpers/custom-logic-worker.ts new file mode 100644 index 0000000000..8b93b15565 --- /dev/null +++ b/guardian-service/src/policy-engine/helpers/custom-logic-worker.ts @@ -0,0 +1,18 @@ +import { workerData, parentPort } from 'node:worker_threads'; +import * as mathjs from 'mathjs'; + +/** + * Execute function + */ +function execute(): void { + const done = (result) => { + parentPort.postMessage(result); + } + + const { execFunc, user, documents, artifacts } = workerData; + + const func = Function(execFunc); + func.apply(documents, [done, user, documents, mathjs, artifacts]); +} + +execute(); diff --git a/guardian-service/src/policy-engine/helpers/decorators/index.ts b/guardian-service/src/policy-engine/helpers/decorators/index.ts index 07097ae2a3..fc5657dfb8 100644 --- a/guardian-service/src/policy-engine/helpers/decorators/index.ts +++ b/guardian-service/src/policy-engine/helpers/decorators/index.ts @@ -11,3 +11,5 @@ export { Report } from './report-block'; export { ReportItem } from './report-item-block'; export { ActionCallback } from './event-callback'; export { ValidatorBlock } from './validator-block'; +export { TokenAddon } from './token-addon'; +export { TokenBlock } from './token-block'; \ No newline at end of file diff --git a/guardian-service/src/policy-engine/helpers/decorators/token-addon.ts b/guardian-service/src/policy-engine/helpers/decorators/token-addon.ts new file mode 100644 index 0000000000..718b0536e7 --- /dev/null +++ b/guardian-service/src/policy-engine/helpers/decorators/token-addon.ts @@ -0,0 +1,35 @@ +import { PolicyBlockDecoratorOptions } from '@policy-engine/interfaces'; +import { BasicBlock } from '@policy-engine/helpers/decorators/basic-block'; +import { IPolicyUser } from '@policy-engine/policy-user'; +import { IHederaAccount } from '../utils'; + +/** + * Token addon + * @param options + * @constructor + */ +export function TokenAddon(options: Partial) { + // tslint:disable-next-line:only-arrow-functions + return function (constructor: new (...args: any) => any): any { + const basicClass = BasicBlock(options)(constructor); + + return class extends basicClass { + + /** + * Block class name + */ + public readonly blockClassName = 'TokenAddon'; + + /** + * Run block logic + * @param scope + */ + public async run(scope: any, root: IHederaAccount, user: IPolicyUser): Promise { + if (typeof super.run === 'function') { + return super.run(scope, root, user); + } + return scope; + } + } + } +} diff --git a/guardian-service/src/policy-engine/helpers/decorators/token-block.ts b/guardian-service/src/policy-engine/helpers/decorators/token-block.ts new file mode 100644 index 0000000000..a226d21997 --- /dev/null +++ b/guardian-service/src/policy-engine/helpers/decorators/token-block.ts @@ -0,0 +1,36 @@ +import { BasicBlock } from '@policy-engine/helpers/decorators/basic-block'; +import { PolicyBlockDecoratorOptions } from '@policy-engine/interfaces/block-options'; +import { IPolicyTokenAddon } from '@policy-engine/policy-engine.interface'; + +/** + * Token block decorator + * @param options + */ +export function TokenBlock(options: Partial) { + // tslint:disable-next-line:only-arrow-functions + return function (constructor: new (...args: any) => any): any { + const basicClass = BasicBlock(options)(constructor); + + return class extends basicClass { + + /** + * Block class name + */ + public readonly blockClassName = 'TokenBlock'; + + /** + * Get block addons + * @protected + */ + protected getAddons(): IPolicyTokenAddon[] { + const addons: IPolicyTokenAddon[] = []; + for (const child of this.children) { + if (child.blockClassName === 'TokenAddon') { + addons.push(child); + } + } + return addons; + } + } + } +} diff --git a/guardian-service/src/policy-engine/helpers/policy-import-export-helper.ts b/guardian-service/src/policy-engine/helpers/policy-import-export-helper.ts index 8c2e0f7b0b..58d7b97211 100644 --- a/guardian-service/src/policy-engine/helpers/policy-import-export-helper.ts +++ b/guardian-service/src/policy-engine/helpers/policy-import-export-helper.ts @@ -14,7 +14,7 @@ import { SchemaEntity, TopicType, GenerateUUIDv4, WorkerTaskType } from '@guardi import { Users } from '@helpers/users'; import { MessageAction, MessageServer, MessageType, PolicyMessage, TopicConfig, TopicHelper } from '@hedera-modules'; import { Topic } from '@entity/topic'; -import { importSchemaByFiles, publishSystemSchema } from '@api/schema.service'; +import { importSchemaByFiles, publishSystemSchemas } from '@api/schema.service'; import { PolicyConverterUtils } from '@policy-engine/policy-converter-utils'; import { INotifier } from '@helpers/notifier'; import { DatabaseServer } from '@database-modules'; @@ -153,7 +153,9 @@ export class PolicyImportExportHelper { DatabaseServer.getSystemSchema(SchemaEntity.WIPE_TOKEN), DatabaseServer.getSystemSchema(SchemaEntity.ISSUER), DatabaseServer.getSystemSchema(SchemaEntity.USER_ROLE), - DatabaseServer.getSystemSchema(SchemaEntity.CHUNK) + DatabaseServer.getSystemSchema(SchemaEntity.CHUNK), + DatabaseServer.getSystemSchema(SchemaEntity.ACTIVITY_IMPACT), + DatabaseServer.getSystemSchema(SchemaEntity.TOKEN_DATA_SOURCE) ]); for (const schema of schemas) { @@ -249,21 +251,9 @@ export class PolicyImportExportHelper { notifier.completedAndStart('Publishing schemas'); const systemSchemas = await PolicyImportExportHelper.getSystemSchemas(); notifier.info(`Found ${systemSchemas.length} schemas`); - let num: number = 0; - for (const schema of systemSchemas) { - messageServer.setTopicObject(topicRow); - let name: string; - if (schema) { - schema.creator = policyOwner; - schema.owner = policyOwner; - const item = await publishSystemSchema(schema, messageServer, MessageAction.PublishSystemSchema); - const newItem = new DataBaseHelper(Schema).create(item); - await new DataBaseHelper(Schema).save(item); - name = newItem.name; - } - num++; - notifier.info(`Schema ${num} (${name || '-'}) published`); - } + messageServer.setTopicObject(topicRow); + + await publishSystemSchemas(systemSchemas, messageServer, policyOwner, notifier); notifier.completed(); diff --git a/guardian-service/src/policy-engine/helpers/property-validator.ts b/guardian-service/src/policy-engine/helpers/property-validator.ts new file mode 100644 index 0000000000..9e129735a4 --- /dev/null +++ b/guardian-service/src/policy-engine/helpers/property-validator.ts @@ -0,0 +1,33 @@ +/** + * Property Validator + */ +export class PropertyValidator { + /** + * Select Type Validator + * @param name + * @param value + * @param requirements + */ + public static selectValidator(name: string, value: string, requirements: any[]): string { + if (!requirements.find(item => item === value)) { + return `Option "${name}" must be one of [${requirements.join(', ')}]`; + } + return null; + } + + /** + * Select Type Validator + * @param name + * @param value + * @param type + */ + public static inputValidator(name: string, value: string, type?: string): string { + if (!value) { + return `Option "${name}" does not set`; + } + if (type && typeof value !== 'string') { + return `Option "${name}" must be a ${type}`; + } + return null; + } +} \ No newline at end of file diff --git a/guardian-service/src/policy-engine/helpers/utils.ts b/guardian-service/src/policy-engine/helpers/utils.ts index b516fe4b68..536486cd7f 100644 --- a/guardian-service/src/policy-engine/helpers/utils.ts +++ b/guardian-service/src/policy-engine/helpers/utils.ts @@ -159,6 +159,7 @@ export class PolicyUtils { let amount = 0; for (const element of vcs) { const scope = PolicyUtils.getVCScope(element); + console.log(' ^^^^^^^^', rule, scope) const value = parseFloat(PolicyUtils.evaluateFormula(rule, scope)); amount += value; } @@ -323,6 +324,25 @@ export class PolicyUtils { return null; } + /** + * Get subject type + * @param document + */ + public static getCredentialSubjectType(document: any): any { + try { + if (document) { + if (Array.isArray(document.credentialSubject)) { + return document.credentialSubject[0]; + } else { + return document.credentialSubject; + } + } + } catch (error) { + return null; + } + return null; + } + /** * Get Document Type * @param document diff --git a/guardian-service/src/policy-engine/interfaces/block-about.ts b/guardian-service/src/policy-engine/interfaces/block-about.ts index 53bac481ab..44e613be73 100644 --- a/guardian-service/src/policy-engine/interfaces/block-about.ts +++ b/guardian-service/src/policy-engine/interfaces/block-about.ts @@ -66,6 +66,10 @@ export interface BlockProperties { * Default value */ default?: any; + /** + * Required fields + */ + required?:boolean; } /** diff --git a/guardian-service/src/policy-engine/interfaces/policy-event-type.ts b/guardian-service/src/policy-engine/interfaces/policy-event-type.ts index 93bccc9087..777d04c2de 100644 --- a/guardian-service/src/policy-engine/interfaces/policy-event-type.ts +++ b/guardian-service/src/policy-engine/interfaces/policy-event-type.ts @@ -9,7 +9,8 @@ export enum PolicyInputEventType { RunEvent = 'RunEvent', ReleaseEvent = 'ReleaseEvent', PopEvent = 'PopEvent', - RestoreEvent = 'RestoreEvent' + RestoreEvent = 'RestoreEvent', + AdditionalMintEvent = 'AdditionalMintEvent' } /** diff --git a/guardian-service/src/policy-engine/policy-engine.interface.ts b/guardian-service/src/policy-engine/policy-engine.interface.ts index 95fc50b49f..113fdea9e9 100644 --- a/guardian-service/src/policy-engine/policy-engine.interface.ts +++ b/guardian-service/src/policy-engine/policy-engine.interface.ts @@ -4,6 +4,7 @@ import { PolicyOutputEventType } from '@policy-engine/interfaces'; import { EventConfig, IPolicyEvent } from './interfaces'; import { DatabaseServer } from '@database-modules'; import { IPolicyUser } from './policy-user'; +import { IHederaAccount } from './helpers/utils'; /** * Policy roles interface @@ -641,6 +642,27 @@ export interface IPolicyValidatorBlock extends IPolicyBlock { run(event: IPolicyEvent): Promise; } +/** + * Policy token block interface + */ +export interface IPolicyTokenBlock extends IPolicyBlock { + /** + * Get addons + */ + getAddons(): IPolicyTokenAddon[]; +} + +/** + * Policy token addon interface + */ +export interface IPolicyTokenAddon extends IPolicyBlock { + /** + * Run logic + * @param scope + */ + run(scope: any, root: IHederaAccount, user: IPolicyUser): Promise; +} + /** * Any block type */ @@ -652,6 +674,8 @@ export type AnyBlockType = | IPolicyAddonBlock | IPolicyCalculateBlock | IPolicyCalculateAddon + | IPolicyTokenBlock + | IPolicyTokenAddon | IPolicyRequestBlock | IPolicyValidatorBlock; @@ -716,6 +740,20 @@ export interface IPolicyState { */ export type IPolicyEventState = IPolicyState; +/** + * Policy document + */ +export interface IPolicyMintEventState { + /** + * Data + */ + data: IPolicyDocument | IPolicyDocument[]; + /** + * Data + */ + result: IPolicyDocument | IPolicyDocument[]; +} + /** * Policy instance */ diff --git a/guardian-service/src/policy-engine/policy-engine.ts b/guardian-service/src/policy-engine/policy-engine.ts index ef30ffddb3..59d6ee773b 100644 --- a/guardian-service/src/policy-engine/policy-engine.ts +++ b/guardian-service/src/policy-engine/policy-engine.ts @@ -28,7 +28,7 @@ import { } from '@hedera-modules' import { findAllEntities, getArtifactType, replaceAllEntities, replaceArtifactProperties, SchemaFields } from '@helpers/utils'; import { IPolicyInstance, IPolicyInterfaceBlock } from './policy-engine.interface'; -import { incrementSchemaVersion, findAndPublishSchema, publishSystemSchema, findAndDryRunSchema, deleteSchema } from '@api/schema.service'; +import { incrementSchemaVersion, findAndPublishSchema, findAndDryRunSchema, deleteSchema, publishSystemSchemas } from '@api/schema.service'; import { PolicyImportExportHelper } from './helpers/policy-import-export-helper'; import { VcHelper } from '@helpers/vc-helper'; import { Users } from '@helpers/users'; @@ -189,18 +189,9 @@ export class PolicyEngine { const systemSchemas = await PolicyImportExportHelper.getSystemSchemas(); notifier.info(`Found ${systemSchemas.length} schemas`); - let num: number = 0; - for (const schema of systemSchemas) { - logger.info('Create Policy: Publish System Schema', ['GUARDIAN_SERVICE']); - messageServer.setTopicObject(topic); - schema.creator = owner; - schema.owner = owner; - const item = await publishSystemSchema(schema, messageServer, MessageAction.PublishSystemSchema); - await DatabaseServer.createAndSaveSchema(item); - const name = item.name; - num++; - notifier.info(`Schema ${num} (${name || '-'}) published`); - } + messageServer.setTopicObject(topic); + + await publishSystemSchemas(systemSchemas, messageServer, owner, notifier); newTopic = await DatabaseServer.saveTopic(topic.toObject()); notifier.completed(); diff --git a/guardian-service/src/policy-engine/policy-validation-results-container.ts b/guardian-service/src/policy-engine/policy-validation-results-container.ts index 5159038614..9d50283f51 100644 --- a/guardian-service/src/policy-engine/policy-validation-results-container.ts +++ b/guardian-service/src/policy-engine/policy-validation-results-container.ts @@ -118,6 +118,19 @@ export class PolicyValidationResultsContainer { block.errors.push(error); } + /** + * Check block error + * @param uuid + * @param error + */ + public checkBlockError(uuid: string, error: string): void { + if (error !== null) { + const block = this.blocks.get(uuid); + block.isValid = false; + block.errors.push(error); + } + } + /** * Add error * @param error diff --git a/guardian-service/system-schemas/system-schemas.json b/guardian-service/system-schemas/system-schemas.json index 30e260c390..fefd0c2112 100644 --- a/guardian-service/system-schemas/system-schemas.json +++ b/guardian-service/system-schemas/system-schemas.json @@ -148,6 +148,116 @@ "additionalProperties": false } }, + { + "uuid": "RetireToken", + "name": "RetireToken", + "entity": "RETIRE_TOKEN", + "document": { + "$id": "#RetireToken", + "$comment": "{ \"term\": \"RetireToken\", \"@id\": \"#RetireToken\" }", + "title": "RetireToken", + "description": "RetireToken", + "type": "object", + "properties": { + "@context": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "readOnly": true + }, + "type": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "readOnly": true + }, + "id": { + "type": "string", + "readOnly": true + }, + "userId": { + "$comment": "{\"term\": \"date\", \"@id\": \"https://www.schema.org/text\"}", + "title": "userId", + "description": "userId", + "type": "string", + "readOnly": false + }, + "baseTokenId": { + "$comment": "{\"term\": \"date\", \"@id\": \"https://www.schema.org/text\"}", + "title": "baseTokenId", + "description": "baseTokenId", + "type": "string", + "readOnly": false + }, + "oppositeTokenId": { + "$comment": "{\"term\": \"amount\", \"@id\": \"https://www.schema.org/text\"}", + "title": "oppositeTokenId", + "description": "oppositeTokenId", + "type": "string", + "readOnly": false + }, + "baseTokenCount": { + "$comment": "{\"term\": \"amount\", \"@id\": \"https://www.schema.org/text\"}", + "title": "baseTokenCount", + "description": "baseTokenCount", + "type": "number", + "readOnly": false + }, + "baseTokenSerials": { + "$comment": "{\"term\": \"amount\", \"@id\": \"https://www.schema.org/text\"}", + "title": "baseTokenSerials", + "description": "baseTokenSerials", + "type": "array", + "items": { + "type": "number" + }, + "readOnly": false + }, + "oppositeTokenCount": { + "$comment": "{\"term\": \"amount\", \"@id\": \"https://www.schema.org/text\"}", + "title": "oppositeTokenCount", + "description": "oppositeTokenCount", + "type": "number", + "readOnly": false + }, + "oppositeTokenSerials": { + "$comment": "{\"term\": \"amount\", \"@id\": \"https://www.schema.org/text\"}", + "title": "oppositeTokenSerials", + "description": "oppositeTokenSerials", + "type": "array", + "items": { + "type": "number" + }, + "readOnly": false + } + }, + "required": [ + "baseTokenId", + "oppositeTokenId", + "baseTokenCount", + "baseTokenSerials", + "oppositeTokenCount", + "oppositeTokenSerials" + ], + "additionalProperties": false + } + }, { "uuid": "Policy", "name": "Policy", @@ -671,5 +781,177 @@ } } } + }, + { + "uuid": "TokenDataSource", + "name": "TokenDataSource", + "entity": "TOKEN_DATA_SOURCE", + "document": { + "$id": "#TokenDataSource", + "$comment": "{ \"term\": \"TokenDataSource\", \"@id\": \"#TokenDataSource\" }", + "title": "TokenDataSource", + "description": "TokenDataSource", + "type": "object", + "properties": { + "@context": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "readOnly": true + }, + "type": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "readOnly": true + }, + "id": { + "type": "string", + "readOnly": true + }, + "dataSource": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "readOnly": false + }, + "relationships": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "readOnly": false + } + }, + "required": [ + "dataSource" + ], + "additionalProperties": false + } + }, + { + "uuid": "ActivityImpact", + "name": "ActivityImpact", + "entity": "ACTIVITY_IMPACT", + "document": { + "$id": "#ActivityImpact", + "$comment": "{ \"term\": \"ActivityImpact\", \"@id\": \"#ActivityImpact\" }", + "title": "ActivityImpact", + "description": "ActivityImpact", + "type": "object", + "properties": { + "@context": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "readOnly": true + }, + "type": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "readOnly": true + }, + "id": { + "type": "string", + "readOnly": true + }, + "impactType": { + "$comment": "{\"term\": \"impactType\", \"@id\": \"https://www.schema.org/text\"}", + "title": "impactType", + "description": "impactType", + "type": "string", + "readOnly": false + }, + "date": { + "$comment": "{\"term\": \"date\", \"@id\": \"https://www.schema.org/text\"}", + "title": "date", + "description": "date", + "type": "string", + "readOnly": false + }, + "amount": { + "$comment": "{\"term\": \"amount\", \"@id\": \"https://www.schema.org/text\"}", + "title": "amount", + "description": "amount", + "type": "string", + "readOnly": false + }, + "unit": { + "$comment": "{\"term\": \"unit\", \"@id\": \"https://www.schema.org/text\"}", + "title": "unit", + "description": "unit", + "type": "string", + "readOnly": false + }, + "label": { + "$comment": "{\"term\": \"label\", \"@id\": \"https://www.schema.org/text\"}", + "title": "label", + "description": "label", + "type": "string", + "readOnly": false + }, + "description": { + "$comment": "{\"term\": \"description\", \"@id\": \"https://www.schema.org/text\"}", + "title": "description", + "description": "description", + "type": "string", + "readOnly": false + } + }, + "required": [ + "date", + "amount", + "impactType" + ], + "additionalProperties": false + } } -] \ No newline at end of file +] diff --git a/interfaces/package.json b/interfaces/package.json index 8972a41054..7191d32c61 100644 --- a/interfaces/package.json +++ b/interfaces/package.json @@ -24,5 +24,6 @@ "lint": "tslint --config ../tslint.json --project .", "test": "echo \"Error: no test specified\" && exit 1" }, - "version": "2.7.0" + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0" } diff --git a/interfaces/src/index.ts b/interfaces/src/index.ts index e7dbf342cb..84cac87e87 100644 --- a/interfaces/src/index.ts +++ b/interfaces/src/index.ts @@ -27,7 +27,16 @@ export { IVPDocument } from './interface/vp-document.interface'; export { IVP } from './interface/vp.interface'; export { IUser, ISession } from './interface/user.interface'; export { IWalletAccount } from './interface/wallet-account.interface'; -export { IChainItem, IReport, IReportItem, IPolicyReport, ITokenReport, IVCReport, IVPReport } from './interface/chain-item.interface'; +export { + IChainItem, + IReport, + IReportItem, + IPolicyReport, + ITokenReport, + IVCReport, + IVPReport, + IImpactReport +} from './interface/chain-item.interface'; export { Token } from './models/token'; export { Schema } from './models/schema'; export { SchemaStatus } from './type/schema-status.type'; @@ -58,3 +67,4 @@ export * from './type/workers.type'; export { ArtifactType } from './type/artifact.type'; export { IArtifact } from './interface/artifact.interface'; export { TokenType } from './type/token.type'; +export { ContractStatus } from './type/contract-status.type'; diff --git a/interfaces/src/interface/chain-item.interface.ts b/interfaces/src/interface/chain-item.interface.ts index 5379aafb67..c26a59d84c 100644 --- a/interfaces/src/interface/chain-item.interface.ts +++ b/interfaces/src/interface/chain-item.interface.ts @@ -106,6 +106,56 @@ export interface IVCReport { document: IVCDocument; } +/** + * Impact report interface + */ +export interface IImpactReport { + /** + * Report type + */ + type: string; + /** + * Report tag + */ + tag: string; + /** + * Report issuer + */ + issuer: string; + /** + * Report username + */ + username: string; + /** + * Report document instance + */ + document: IVCDocument; + /** + * Impact Type + */ + impactType: string; + /** + * Impact label + */ + label: string; + /** + * Impact description + */ + description: string; + /** + * Impact amount + */ + amount: string; + /** + * Impact amount unit + */ + unit: string; + /** + * Impact date + */ + date: string; +} + /** * Token report interface */ @@ -122,6 +172,10 @@ export interface ITokenReport { * Report date */ date: string; + /** + * Token amount + */ + amount: string; /** * Report tag */ @@ -256,4 +310,29 @@ export interface IReport { * Report items */ documents?: IReportItem[]; + /** + * Impacts + */ + impacts?: IImpactReport[]; + /** + * Additional Documents + */ + additionalDocuments?: { + /** + * VP document instance + */ + vpDocument?: IVPReport; + /** + * VC document instance + */ + vcDocument?: IVCReport; + /** + * Mint document instance + */ + mintDocument?: ITokenReport; + /** + * Impacts + */ + impacts?: IImpactReport[]; + }[]; } diff --git a/interfaces/src/interface/token.interface.ts b/interfaces/src/interface/token.interface.ts index b1f5d67ee6..6145af3499 100644 --- a/interfaces/src/interface/token.interface.ts +++ b/interfaces/src/interface/token.interface.ts @@ -1,3 +1,5 @@ +import { TokenType } from '../type/token.type'; + /** * Token interface */ @@ -21,7 +23,7 @@ export interface IToken { /** * Type */ - tokenType?: string; + tokenType?: TokenType; /** * Decimals */ diff --git a/interfaces/src/models/token.ts b/interfaces/src/models/token.ts index eed5946bd2..12a15e0e81 100644 --- a/interfaces/src/models/token.ts +++ b/interfaces/src/models/token.ts @@ -57,9 +57,21 @@ export class Token { */ public url: string; /** - * URL + * policies */ public policies: string[]; + /** + * Token type + */ + public tokenType?: string; + /** + * Token decimals + */ + public decimals: any; + /** + * Initial supply + */ + public initialSupply?: any; /** * Token constructor * @param data @@ -69,6 +81,9 @@ export class Token { this.tokenId = data.tokenId; this.tokenName = data.tokenName; this.tokenSymbol = data.tokenSymbol; + this.tokenType = data.tokenType; + this.decimals = data.decimals; + this.initialSupply = data.initialSupply; this.enableAdmin = data.enableAdmin; this.enableFreeze = data.enableFreeze; this.enableKYC = data.enableKYC; diff --git a/interfaces/src/type/contract-status.type.ts b/interfaces/src/type/contract-status.type.ts new file mode 100644 index 0000000000..2c4e1a8d60 --- /dev/null +++ b/interfaces/src/type/contract-status.type.ts @@ -0,0 +1,7 @@ +/** + * Contract Status + */ +export enum ContractStatus { + WAIT = 'WAIT', + APPROVED = 'APPROVED' +} \ No newline at end of file diff --git a/interfaces/src/type/message-api.type.ts b/interfaces/src/type/message-api.type.ts index 3f5a8154b1..13e3a3deb7 100644 --- a/interfaces/src/type/message-api.type.ts +++ b/interfaces/src/type/message-api.type.ts @@ -76,7 +76,18 @@ export enum MessageAPI { UPLOAD_ARTIFACT = 'UPLOAD_ARTIFACT', GET_ARTIFACTS = 'GET_ARTIFACTS', DELETE_ARTIFACT = 'DELETE_ARTIFACT', - GET_ALL_USER_TOPICS_ASYNC = 'GET_ALL_USER_TOPICS_ASYNC' + GET_ALL_USER_TOPICS_ASYNC = 'GET_ALL_USER_TOPICS_ASYNC', + GET_CONTRACT = 'GET_CONTRACT', + CREATE_CONTRACT = 'CREATE_CONTRACT', + IMPORT_CONTRACT = 'IMPORT_CONTRACT', + GET_CONTRACT_PAIR = 'GET_CONTRACT_PAIR', + CHECK_CONTRACT_STATUS = 'CHECK_CONTRACT_STATUS', + ADD_CONTRACT_PAIR = 'ADD_CONTRACT_PAIR', + ADD_CONTRACT_USER = 'ADD_CONTRACT_USER', + ADD_RETIRE_REQUEST = 'ADD_RETIRE_REQUEST', + GET_RETIRE_REQUEST = 'GET_RETIRE_REQUEST', + RETIRE_TOKENS = 'RETIRE_TOKENS', + CANCEL_RETIRE_REQUEST = 'CANCEL_RETIRE_REQUEST', } /** diff --git a/interfaces/src/type/schema-entity.type.ts b/interfaces/src/type/schema-entity.type.ts index 80ed64a85e..689911b026 100644 --- a/interfaces/src/type/schema-entity.type.ts +++ b/interfaces/src/type/schema-entity.type.ts @@ -8,9 +8,12 @@ export enum SchemaEntity { USER = 'USER', POLICY = 'POLICY', MINT_TOKEN = 'MINT_TOKEN', + RETIRE_TOKEN = 'RETIRE_TOKEN', WIPE_TOKEN = 'WIPE_TOKEN', MINT_NFTOKEN = 'MINT_NFTOKEN', ISSUER = 'ISSUER', USER_ROLE = 'USER_ROLE', - CHUNK = 'CHUNK' + CHUNK = 'CHUNK', + ACTIVITY_IMPACT = 'ACTIVITY_IMPACT', + TOKEN_DATA_SOURCE = 'TOKEN_DATA_SOURCE' } diff --git a/interfaces/src/type/topic.type.ts b/interfaces/src/type/topic.type.ts index fdca23d7c6..0b5bb3ae5f 100644 --- a/interfaces/src/type/topic.type.ts +++ b/interfaces/src/type/topic.type.ts @@ -7,5 +7,6 @@ export enum TopicType { InstancePolicyTopic = 'INSTANCE_POLICY_TOPIC', DynamicTopic = 'DYNAMIC_TOPIC', SchemaTopic = 'SCHEMA_TOPIC', - SynchronizationTopic = 'SYNCHRONIZATION_TOPIC' + SynchronizationTopic = 'SYNCHRONIZATION_TOPIC', + RetireTopic = 'RETIRE_TOPIC', } diff --git a/interfaces/src/type/workers.type.ts b/interfaces/src/type/workers.type.ts index 8bc8ae14d8..236c6962ab 100644 --- a/interfaces/src/type/workers.type.ts +++ b/interfaces/src/type/workers.type.ts @@ -22,7 +22,19 @@ export enum WorkerTaskType { NEW_TOPIC = 'new-topic', CHECK_ACCOUNT = 'check-account', GET_TOPIC_MESSAGE = 'get-topic-message', - GET_TOPIC_MESSAGES = 'get-topic-messages' + GET_TOPIC_MESSAGES = 'get-topic-messages', + CREATE_CONTRACT = 'create-contract', + ADD_CONTRACT_USER = 'add-contract-user', + CHECK_STATUS = 'check-status', + GET_CONTRACT_INFO = 'get-contract-info', + ADD_CONTRACT_PAIR = 'add-contract-pair', + GET_CONTRACT_PAIR = 'get-contract-pair', + ADD_RETIRE_REQUEST = 'add-retire-request', + GET_RETIRE_REQUEST = 'get-retire-request', + RETIRE_TOKENS = 'retire-tokens', + CANCEL_RETIRE_REQUEST = 'cancel-retire-request', + GET_USER_NFTS_SERIALS = 'get-user-nfts-serials', + HTTP_REQUEST = 'http-request', } /** diff --git a/logger-service/.env b/logger-service/.env index 527ea298b0..eefa7e2e49 100644 --- a/logger-service/.env +++ b/logger-service/.env @@ -1,4 +1,5 @@ MQ_ADDRESS="localhost" SERVICE_CHANNEL="logger-service" DB_HOST="localhost" -DB_DATABASE="logger_db" \ No newline at end of file +DB_DATABASE="logger_db" +#MQ_MESSAGE_CHUNK=5000000 \ No newline at end of file diff --git a/logger-service/.env.docker b/logger-service/.env.docker index 9cc8dbf6da..475779948c 100644 --- a/logger-service/.env.docker +++ b/logger-service/.env.docker @@ -1,4 +1,5 @@ MQ_ADDRESS="message-broker" SERVICE_CHANNEL="logger-service" DB_HOST="mongo" -DB_DATABASE="logger_db" \ No newline at end of file +DB_DATABASE="logger_db" +#MQ_MESSAGE_CHUNK=5000000 \ No newline at end of file diff --git a/logger-service/package.json b/logger-service/package.json index eb97dc4d13..2de2b3fda6 100644 --- a/logger-service/package.json +++ b/logger-service/package.json @@ -1,8 +1,8 @@ { "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.7.0", - "@guardian/interfaces": "^2.7.0", + "@guardian/common": "^2.8.0-prerelease", + "@guardian/interfaces": "^2.8.0-prerelease", "@mikro-orm/core": "~5.3.0", "@mikro-orm/mongodb": "~5.3.0", "@web-std/fetch": "3.0.0", @@ -36,5 +36,6 @@ "start": "node dist/index.js", "watch": "nodemon src/index.ts" }, - "version": "2.7.0" + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0" } diff --git a/mrv-sender/package.json b/mrv-sender/package.json index 9d075f4ea4..ff5bacf0d1 100644 --- a/mrv-sender/package.json +++ b/mrv-sender/package.json @@ -1,7 +1,7 @@ { "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.7.0", + "@guardian/common": "^2.8.0-prerelease", "@transmute/credentials-context": "0.7.0-unstable.40", "@transmute/did-context": "0.7.0-unstable.40", "@transmute/ed25519-signature-2018": "0.7.0-unstable.40", @@ -29,5 +29,6 @@ "dev:docker": "nodemon .", "start": "node dist/index.js" }, - "version": "2.7.0" + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0" } diff --git a/package.json b/package.json index 9f1a09d02f..d48d538478 100644 --- a/package.json +++ b/package.json @@ -13,5 +13,6 @@ "mrv-sender", "worker-service" ], - "version": "2.7.0" + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0" } diff --git a/threads-test/package.json b/threads-test/package.json index a7da7d01b6..1009f488c2 100644 --- a/threads-test/package.json +++ b/threads-test/package.json @@ -15,7 +15,7 @@ "@types/node": "^18.11.9", "typescript": "^4.6.3", "@hashgraph/did-sdk-js": "0.1.1", - "@hashgraph/sdk": "^2.18.3", + "@hashgraph/sdk": "^2.19.0", "@transmute/credentials-context": "0.7.0-unstable.40", "@transmute/did-context": "0.7.0-unstable.40", "@transmute/ed25519-signature-2018": "0.7.0-unstable.40", diff --git a/topic-viewer/package.json b/topic-viewer/package.json index b2247b25b1..db909ac429 100644 --- a/topic-viewer/package.json +++ b/topic-viewer/package.json @@ -19,5 +19,6 @@ "dev": "tsc -w", "start": "node dist/index.js" }, - "version": "2.7.0" + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0" } diff --git a/web-proxy/configs/default.conf b/web-proxy/configs/default.conf index 6cf7d9a93d..311220cdf9 100644 --- a/web-proxy/configs/default.conf +++ b/web-proxy/configs/default.conf @@ -26,6 +26,7 @@ server { proxy_read_timeout 1200s; proxy_connect_timeout 1200s; proxy_send_timeout 1200s; + client_max_body_size 1024m; } location /mrv-sender/ { diff --git a/web-proxy/configs/demo.conf b/web-proxy/configs/demo.conf index bcf57472ef..808bac7e9c 100644 --- a/web-proxy/configs/demo.conf +++ b/web-proxy/configs/demo.conf @@ -26,6 +26,7 @@ server { proxy_read_timeout 600s; proxy_connect_timeout 600s; proxy_send_timeout 600s; + client_max_body_size ${GATEWAY_CLIENT_MAX_BODY_SIZE}; } location /mrv-sender/ { diff --git a/web-proxy/configs/image.conf b/web-proxy/configs/image.conf index f3f1c94744..abbf26de3b 100644 --- a/web-proxy/configs/image.conf +++ b/web-proxy/configs/image.conf @@ -26,6 +26,7 @@ server { proxy_read_timeout 600s; proxy_connect_timeout 600s; proxy_send_timeout 600s; + client_max_body_size ${GATEWAY_CLIENT_MAX_BODY_SIZE}; } location / { diff --git a/web-proxy/configs/local.conf b/web-proxy/configs/local.conf index 0acdaebc7a..a023977870 100644 --- a/web-proxy/configs/local.conf +++ b/web-proxy/configs/local.conf @@ -26,6 +26,7 @@ server { proxy_read_timeout 600s; proxy_connect_timeout 600s; proxy_send_timeout 600s; + client_max_body_size 1024m; } location /mrv-sender/ { diff --git a/worker-service/.env b/worker-service/.env index ce673d7f0b..75d1e8fc52 100644 --- a/worker-service/.env +++ b/worker-service/.env @@ -9,3 +9,4 @@ IPFS_PROVIDER="web3storage" # 'web3storage' or 'local' IPFS_PUBLIC_GATEWAY="https://ipfs.io/ipfs/${cid}" IPFS_STORAGE_API_KEY="..." IPFS_NODE_ADDRESS="http://localhost:5002" +#MQ_MESSAGE_CHUNK=5000000 diff --git a/worker-service/.env.docker b/worker-service/.env.docker index c8003ce2cf..a6948420ea 100644 --- a/worker-service/.env.docker +++ b/worker-service/.env.docker @@ -9,3 +9,4 @@ IPFS_PROVIDER="web3storage" # 'web3storage' or 'local' IPFS_PUBLIC_GATEWAY="https://ipfs.io/ipfs/${cid}" IPFS_STORAGE_API_KEY="..." IPFS_NODE_ADDRESS="http://ipfs-node:5002" +#MQ_MESSAGE_CHUNK=5000000 diff --git a/worker-service/package.json b/worker-service/package.json index e5393b4ffc..4a99de028e 100644 --- a/worker-service/package.json +++ b/worker-service/package.json @@ -1,9 +1,9 @@ { "author": "Envision Blockchain Solutions ", "dependencies": { - "@guardian/common": "^2.7.0", - "@guardian/interfaces": "^2.7.0", - "@hashgraph/sdk": "^2.18.3", + "@guardian/common": "^2.8.0-prerelease", + "@guardian/interfaces": "^2.8.0-prerelease", + "@hashgraph/sdk": "^2.19.0", "@transmute/credentials-context": "^0.7.0-unstable.60", "@transmute/did-context": "^0.7.0-unstable.60", "@transmute/ed25519-signature-2018": "^0.7.0-unstable.60", @@ -47,5 +47,6 @@ "start": "node dist/index.js", "test": "mocha tests/**/*.test.js --reporter mocha-junit-reporter --reporter-options mochaFile=../test_results/worker-service.xml --exit" }, - "version": "2.7.0" + "version": "2.8.0-prerelease", + "stableVersion": "2.7.0" } diff --git a/worker-service/src/api/helpers/environment.ts b/worker-service/src/api/helpers/environment.ts index d63c563873..0cd24cffaf 100644 --- a/worker-service/src/api/helpers/environment.ts +++ b/worker-service/src/api/helpers/environment.ts @@ -12,6 +12,10 @@ export class Environment { * Mainnet topic API */ public static readonly HEDERA_MAINNET_TOPIC_API: string = 'https://mainnet-public.mirrornode.hedera.com/api/v1/topics/'; + /** + * Mainnet account API + */ + public static readonly HEDERA_MAINNET_ACCOUNT_API: string = 'https://mainnet-public.mirrornode.hedera.com/api/v1/accounts/'; /** * Testnar message API */ @@ -20,6 +24,10 @@ export class Environment { * Testnet topic API */ public static readonly HEDERA_TESTNET_TOPIC_API: string = 'https://testnet.mirrornode.hedera.com/api/v1/topics/'; + /** + * Testnet account API + */ + public static readonly HEDERA_TESTNET_ACCOUNT_API: string = 'https://testnet.mirrornode.hedera.com/api/v1/accounts/'; /** * Localnode message API */ @@ -28,6 +36,10 @@ export class Environment { * Localnode topic API */ public static HEDERA_LOCALNODE_TOPIC_API: string = `https://localhost:5551/api/v1/topics/`; + /** + * Localnode topic API + */ + public static HEDERA_LOCALNODE_ACCOUNT_API: string = `https://localhost:5551/api/v1/accounts/`; /** * Localnode protocol */ @@ -52,6 +64,11 @@ export class Environment { * @private */ private static _topicsApi: string = Environment.HEDERA_TESTNET_TOPIC_API; + /** + * Account API + * @private + */ + private static _accountsApi: string = Environment.HEDERA_TESTNET_ACCOUNT_API; /** * Set network @@ -64,18 +81,21 @@ export class Environment { Environment._network = 'mainnet'; Environment._messagesApi = Environment.HEDERA_MAINNET_MESSAGE_API; Environment._topicsApi = Environment.HEDERA_MAINNET_TOPIC_API; + Environment._accountsApi = Environment.HEDERA_MAINNET_ACCOUNT_API; break; case 'testnet': Environment._network = 'testnet'; Environment._messagesApi = Environment.HEDERA_TESTNET_MESSAGE_API; Environment._topicsApi = Environment.HEDERA_TESTNET_TOPIC_API; + Environment._accountsApi = Environment.HEDERA_TESTNET_ACCOUNT_API; break; case 'localnode': Environment._network = 'localnode'; Environment._messagesApi = Environment.HEDERA_LOCALNODE_MESSAGE_API; Environment._topicsApi = Environment.HEDERA_LOCALNODE_TOPIC_API; + Environment._accountsApi = Environment.HEDERA_LOCALNODE_ACCOUNT_API; break; default: @@ -85,6 +105,7 @@ export class Environment { if (mirrornode) { Environment._messagesApi = `${mirrornode}/api/v1/topics/messages`; Environment._topicsApi = `${mirrornode}/api/v1/topics/`; + Environment._accountsApi = `${mirrornode}/api/v1/accounts/`; } } @@ -95,6 +116,7 @@ export class Environment { Environment._localnodeaddress = address || 'localhost'; Environment.HEDERA_LOCALNODE_MESSAGE_API = `${Environment._localnodeprotocol}://${Environment._localnodeaddress}:5551/api/v1/topics/messages`; Environment.HEDERA_LOCALNODE_TOPIC_API = `${Environment._localnodeprotocol}://${Environment._localnodeaddress}:5551/api/v1/topics/`; + Environment.HEDERA_LOCALNODE_ACCOUNT_API = `${Environment._localnodeprotocol}://${Environment._localnodeaddress}:5551/api/v1/accounts/`; } /** @@ -147,4 +169,12 @@ export class Environment { public static get HEDERA_TOPIC_API(): string { return Environment._topicsApi; } + + /** + * Hedera account API + * @constructor + */ + public static get HEDERA_ACCOUNT_API(): string { + return Environment._accountsApi; + } } diff --git a/worker-service/src/api/helpers/hedera-sdk-helper.ts b/worker-service/src/api/helpers/hedera-sdk-helper.ts index 19016847ff..8817322646 100644 --- a/worker-service/src/api/helpers/hedera-sdk-helper.ts +++ b/worker-service/src/api/helpers/hedera-sdk-helper.ts @@ -4,6 +4,15 @@ import { AccountId, AccountInfoQuery, Client, + ContractCallQuery, + ContractCreateTransaction, + ContractExecuteTransaction, + ContractFunctionParameters, + ContractFunctionResult, + ContractId, + ContractInfo, + ContractInfoQuery, + FileId, Hbar, HbarUnit, PrivateKey, @@ -1041,6 +1050,152 @@ export class HederaSDKHelper { return NaN; } + /** + * Create Hedera Smart Contract + * + * @param {string | FileId} bytecodeFileId - Code File Id + * @param {ContractFunctionParameters} parameters - Contract Parameters + * + * @returns {string} - Contract Id + */ + @timeout(HederaSDKHelper.MAX_TIMEOUT) + public async createContract( + bytecodeFileId: string | FileId, + parameters: ContractFunctionParameters + ): Promise { + const client = this.client; + const contractInstantiateTx = new ContractCreateTransaction() + .setBytecodeFileId(bytecodeFileId) + .setGas(1000000) + .setConstructorParameters(parameters); + const contractInstantiateSubmit = await contractInstantiateTx.execute( + client + ); + const contractInstantiateRx = + await contractInstantiateSubmit.getReceipt(client); + const contractId = contractInstantiateRx.contractId; + return `${contractId}`; + } + + /** + * Query Contract Hedera + * + * @param {string | ContractId} contractId - Contract Id + * @param {string} functionName - Function Name + * @param {ContractFunctionParameters} parameters - Contract Parameters + * + * @returns {ContractFunctionResult} - Contract Query Result + */ + @timeout(HederaSDKHelper.MAX_TIMEOUT) + public async contractQuery( + contractId: string | ContractId, + functionName: string, + parameters: ContractFunctionParameters + ): Promise { + const client = this.client; + const contractQueryTx = new ContractCallQuery() + .setContractId(contractId) + .setGas(100000) + .setFunction(functionName, parameters); + const contractQueryResult = await contractQueryTx.execute(client); + return contractQueryResult; + } + + /** + * Call Contract Hedera + * + * @param {string | ContractId} contractId - Contract Id + * @param {string} functionName - Function Name + * @param {ContractFunctionParameters} parameters - Contract Parameters + * @param {string[]} additionalKeys - Additional Keys + * + * @returns {boolean} - Status + */ + @timeout(HederaSDKHelper.MAX_TIMEOUT) + public async contractCall( + contractId: string | ContractId, + functionName: string, + parameters: ContractFunctionParameters, + additionalKeys?: string[] + ): Promise { + const client = this.client; + let contractExecuteTx: any = await new ContractExecuteTransaction() + .setContractId(contractId) + .setGas(2000000) + .setFunction(functionName, parameters) + .freezeWith(client); + if (additionalKeys && additionalKeys.length) { + for (const key of additionalKeys) { + contractExecuteTx = await contractExecuteTx.sign( + HederaUtils.parsPrivateKey(key, true) + ); + } + } + const contractExecuteSubmit = await contractExecuteTx.execute(client); + const contractExecuteRx = await contractExecuteSubmit.getReceipt( + client + ); + return contractExecuteRx.status === Status.Success; + } + + /** + * Get Contract Info + * + * @param {string | ContractId} contractId - Contract Id + * + * @returns {any} - Contract Info + */ + @timeout(HederaSDKHelper.MAX_TIMEOUT) + public async getContractInfo( + contractId: string | ContractId, + ): Promise { + const client = this.client; + const query = new ContractInfoQuery().setContractId(contractId); + return await query.execute(client); + } + + /** + * Get NFT serials + * @param tokenId Token identifier + * @returns Serials Info + */ + @timeout(HederaSDKHelper.MAX_TIMEOUT) + public async getSerialsNFT(tokenId?: string): Promise { + let goNext = true; + const client = this.client; + let url = `${Environment.HEDERA_ACCOUNT_API}${client.operatorAccountId}/nfts`; + const result = []; + const p = { + params: { + limit: Number.MAX_SAFE_INTEGER, + tokenId, + }, + responseType: 'json', + }; + while (goNext) { + const res = await axios.get(url, p as any); + delete p.params; + + if (!res || !res.data || !res.data.nfts) { + throw new Error(`Invalid nfts serials response`); + } + + const nfts = res.data.nfts; + if (nfts.length === 0) { + return result; + } + + result.push(...nfts); + if (res.data.links?.next) { + url = `${res.request.protocol}//${res.request.host}${res.data.links?.next}`; + } else { + goNext = false; + } + } + + return result; + } + /** * Check Account * @param accountId diff --git a/worker-service/src/api/worker.ts b/worker-service/src/api/worker.ts index 634e1d0ee0..b96c8d6d63 100644 --- a/worker-service/src/api/worker.ts +++ b/worker-service/src/api/worker.ts @@ -11,8 +11,9 @@ import { HederaSDKHelper } from './helpers/hedera-sdk-helper'; import { Environment } from './helpers/environment'; import { IpfsClient } from './ipfs-client'; import Blob from 'cross-blob'; -import { AccountId, PrivateKey, TokenId } from '@hashgraph/sdk'; +import { AccountId, ContractFunctionParameters, PrivateKey, TokenId } from '@hashgraph/sdk'; import { HederaUtils } from './helpers/utils'; +import axios from 'axios'; /** * Sleep helper @@ -92,14 +93,9 @@ export class Worker { */ private readonly taskTimeout: number; - /** - * Channel Name - * @private - */ - private readonly _channelName: string; - constructor( - private readonly channel: MessageBrokerChannel + private readonly channel: MessageBrokerChannel, + private readonly channelName: string ) { const { IPFS_STORAGE_API_KEY } = new SettingsContainer().settings; @@ -109,7 +105,6 @@ export class Worker { this.minPriority = parseInt(process.env.MIN_PRIORITY, 10); this.maxPriority = parseInt(process.env.MAX_PRIORITY, 10); this.taskTimeout = parseInt(process.env.TASK_TIMEOUT, 10) * 1000; // env in seconds - this._channelName = process.env.SERVICE_CHANNEL.toUpperCase(); } /** @@ -236,6 +231,18 @@ export class Worker { break; } + case WorkerTaskType.HTTP_REQUEST: { + const { method, url, headers, body } = task.data.payload; + const response = await axios({ + method, + url, + headers, + data: body + }); + result.data = response.data; + break; + } + case WorkerTaskType.SEND_HEDERA: { Environment.setNetwork(task.data.network); Environment.setLocalNodeAddress(task.data.localNodeAddress); @@ -574,6 +581,261 @@ export class Worker { break; } + case WorkerTaskType.CREATE_CONTRACT: { + const { + hederaAccountId, + hederaAccountKey, + topicKey, + bytecodeFileId, + } = task.data; + const client = new HederaSDKHelper( + hederaAccountId, + hederaAccountKey + ); + result.data = await client.createContract( + bytecodeFileId, + new ContractFunctionParameters().addString(topicKey) + ); + break; + } + + case WorkerTaskType.ADD_CONTRACT_USER: { + const { + hederaAccountId, + hederaAccountKey, + userId, + contractId, + } = task.data; + const client = new HederaSDKHelper( + hederaAccountId, + hederaAccountKey + ); + result.data = await client.contractCall( + contractId, 'addUser', + new ContractFunctionParameters().addAddress(AccountId.fromString(userId).toSolidityAddress()) + ); + break; + } + + case WorkerTaskType.ADD_CONTRACT_PAIR: { + const { + hederaAccountId, + hederaAccountKey, + contractId, + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount, + grantKycKeys, + } = task.data; + const client = new HederaSDKHelper( + hederaAccountId, + hederaAccountKey + ); + result.data = await client.contractCall( + contractId, 'addPair', + new ContractFunctionParameters() + .addAddress(TokenId.fromString(baseTokenId).toSolidityAddress()) + .addAddress(TokenId.fromString(oppositeTokenId).toSolidityAddress()) + .addUint32(Math.floor(baseTokenCount)) + .addUint32(Math.floor(oppositeTokenCount)), + grantKycKeys + ); + break; + } + + case WorkerTaskType.GET_CONTRACT_INFO: { + const { + hederaAccountId, + hederaAccountKey, + contractId, + } = task.data; + const client = new HederaSDKHelper( + hederaAccountId, + hederaAccountKey + ); + result.data = AccountId.fromSolidityAddress( + ( + await client.contractQuery( + contractId, + 'getOwner', + new ContractFunctionParameters() + ) + ).getAddress() + ).toString(); + break; + } + + case WorkerTaskType.CHECK_STATUS: { + const { + hederaAccountId, + hederaAccountKey, + contractId, + } = task.data; + const client = new HederaSDKHelper( + hederaAccountId, + hederaAccountKey + ); + result.data = (await client.contractQuery( + contractId, 'checkStatus', + new ContractFunctionParameters() + )).getBool(); + break; + } + + case WorkerTaskType.RETIRE_TOKENS: { + const { + hederaAccountId, + hederaAccountKey, + baseTokenId, + oppositeTokenId, + userId, + contractId, + wipeKeys + } = task.data; + const client = new HederaSDKHelper( + hederaAccountId, + hederaAccountKey + ); + result.data = await client.contractCall( + contractId, 'retire', + (new ContractFunctionParameters() + .addAddress(AccountId.fromString(userId).toSolidityAddress()) + .addAddress(TokenId.fromString(baseTokenId).toSolidityAddress()) + .addAddress(TokenId.fromString(oppositeTokenId).toSolidityAddress()) + ), + wipeKeys + ); + break; + } + + case WorkerTaskType.CANCEL_RETIRE_REQUEST: { + const { + hederaAccountId, + hederaAccountKey, + baseTokenId, + oppositeTokenId, + contractId + } = task.data; + const client = new HederaSDKHelper( + hederaAccountId, + hederaAccountKey + ); + result.data = await client.contractCall( + contractId, 'cancelUserRequest', + (new ContractFunctionParameters() + .addAddress(TokenId.fromString(baseTokenId).toSolidityAddress()) + .addAddress(TokenId.fromString(oppositeTokenId).toSolidityAddress()) + ) + ); + break; + } + + case WorkerTaskType.GET_CONTRACT_PAIR: { + const { + hederaAccountId, + hederaAccountKey, + baseTokenId, + oppositeTokenId, + contractId + } = task.data; + const client = new HederaSDKHelper( + hederaAccountId, + hederaAccountKey + ); + const contractQueryResult = await client.contractQuery( + contractId, 'getPair', + (new ContractFunctionParameters() + .addAddress(TokenId.fromString(baseTokenId).toSolidityAddress()) + .addAddress(TokenId.fromString(oppositeTokenId).toSolidityAddress()) + ) + ); + result.data = { + baseTokenRate: contractQueryResult.getUint32(0), + oppositeTokenRate: contractQueryResult.getUint32(1), + contractId + }; + break; + } + + case WorkerTaskType.ADD_RETIRE_REQUEST: { + const { + hederaAccountId, + hederaAccountKey, + baseTokenId, + oppositeTokenId, + baseTokenCount, + oppositeTokenCount, + baseTokenSerials, + oppositeTokenSerials, + contractId + } = task.data; + const client = new HederaSDKHelper( + hederaAccountId, + hederaAccountKey + ); + result.data = await client.contractCall( + contractId, 'addUserRequest', + (new ContractFunctionParameters() + .addAddress(TokenId.fromString(baseTokenId).toSolidityAddress()) + .addAddress(TokenId.fromString(oppositeTokenId).toSolidityAddress()) + .addUint32(baseTokenCount) + .addUint32(oppositeTokenCount) + .addInt64Array(baseTokenSerials && baseTokenSerials.length ? baseTokenSerials : [0]) + .addInt64Array(oppositeTokenSerials && oppositeTokenSerials.length ? oppositeTokenSerials : [0]) + ) + ); + break; + } + + case WorkerTaskType.GET_RETIRE_REQUEST: { + const { + hederaAccountId, + hederaAccountKey, + baseTokenId, + oppositeTokenId, + userId, + contractId + } = task.data; + const client = new HederaSDKHelper( + hederaAccountId, + hederaAccountKey + ); + const contractQueryResult = await client.contractQuery( + contractId, 'getUserRequest', + (new ContractFunctionParameters() + .addAddress(AccountId.fromString(userId).toSolidityAddress()) + .addAddress(TokenId.fromString(baseTokenId).toSolidityAddress()) + .addAddress(TokenId.fromString(oppositeTokenId).toSolidityAddress()) + ) + ); + result.data = { + baseTokenCount: contractQueryResult.getUint32(0) || contractQueryResult.getUint32(2), + oppositeTokenCount: contractQueryResult.getUint32(1) || contractQueryResult.getUint32(3) + } + break; + } + + case WorkerTaskType.GET_USER_NFTS_SERIALS: { + const { + operatorId, + operatorKey, + tokenId, + } = task.data; + const client = new HederaSDKHelper(operatorId, operatorKey); + const nfts = (await client.getSerialsNFT(tokenId)) || []; + const serials = {}; + nfts.forEach(item => { + if (serials[item.token_id]) { + serials[item.token_id].push(item.serial_number); + } else { + serials[item.token_id] = [item.serial_number]; + } + }); + result.data = serials; + break; + } + default: result.error = 'unknown task' } @@ -617,7 +879,7 @@ export class Worker { public async getItem(): Promise { this.isInUse = true; - this.logger.info(`Search task`, [this._channelName]); + this.logger.info(`Search task`, [this.channelName]); let task: any = null; try { @@ -637,7 +899,7 @@ export class Worker { if (!task) { this.isInUse = false; - this.logger.info(`Task not found`, [this._channelName]); + this.logger.info(`Task not found`, [this.channelName]); if (this.updateEventReceived) { this.updateEventReceived = false; @@ -649,19 +911,19 @@ export class Worker { this.currentTaskId = task.id; - this.logger.info(`Task started: ${task.id}, ${task.type}`, [this._channelName]); + this.logger.info(`Task started: ${task.id}, ${task.type}`, [this.channelName]); const result = await this.processTaskWithTimeout(task); try { await this.request(WorkerEvents.TASK_COMPLETE, result); if (result?.error) { - this.logger.error(`Task error: ${this.currentTaskId}, ${result?.error}`, [this._channelName]); + this.logger.error(`Task error: ${this.currentTaskId}, ${result?.error}`, [this.channelName]); } else { - this.logger.info(`Task completed: ${this.currentTaskId}`, [this._channelName]); + this.logger.info(`Task completed: ${this.currentTaskId}`, [this.channelName]); } } catch (error) { - this.logger.error(error.message, [this._channelName]); + this.logger.error(error.message, [this.channelName]); this.clearState(); } diff --git a/worker-service/src/app.ts b/worker-service/src/app.ts index f70b3ae952..3751a1cab0 100644 --- a/worker-service/src/app.ts +++ b/worker-service/src/app.ts @@ -5,12 +5,13 @@ import { HederaSDKHelper } from './api/helpers/hedera-sdk-helper'; Promise.all([ MessageBrokerChannel.connect('WORKERS_SERVICE') ]).then(async values => { + const channelName = (process.env.SERVICE_CHANNEL || `worker.${Date.now()}`).toUpperCase() const [cn] = values; const channel = new MessageBrokerChannel(cn, 'worker'); const logger = new Logger(); logger.setChannel(channel); - const state = new ApplicationState(process.env.SERVICE_CHANNEL.toUpperCase()); + const state = new ApplicationState(channelName); state.setChannel(channel); HederaSDKHelper.setTransactionLogSender(async (data) => { @@ -21,10 +22,10 @@ Promise.all([ settingsContainer.setChannel(channel); await settingsContainer.init('IPFS_STORAGE_API_KEY'); - const w = new Worker(channel); + const w = new Worker(channel, channelName); w.init(); - logger.info('Worker started', [process.env.SERVICE_CHANNEL.toUpperCase()]); + logger.info('Worker started', [channelName]); }, (reason) => { console.log(reason); process.exit(0); diff --git a/yarn.lock b/yarn.lock index 6a1c73be4f..3ae920a964 100644 --- a/yarn.lock +++ b/yarn.lock @@ -187,11 +187,11 @@ __metadata: languageName: node linkType: hard -"@guardian/common@^2.7.0, @guardian/common@workspace:common": +"@guardian/common@^2.8.0-prerelease, @guardian/common@workspace:common": version: 0.0.0-use.local resolution: "@guardian/common@workspace:common" dependencies: - "@guardian/interfaces": ^2.7.0 + "@guardian/interfaces": ^2.8.0-prerelease "@mikro-orm/core": ~5.3.0 "@mikro-orm/migrations-mongodb": ~5.3.0 "@mikro-orm/mongodb": ~5.3.0 @@ -209,7 +209,7 @@ __metadata: languageName: unknown linkType: soft -"@guardian/interfaces@^2.7.0, @guardian/interfaces@workspace:interfaces": +"@guardian/interfaces@^2.8.0-prerelease, @guardian/interfaces@workspace:interfaces": version: 0.0.0-use.local resolution: "@guardian/interfaces@workspace:interfaces" dependencies: @@ -246,24 +246,24 @@ __metadata: languageName: node linkType: hard -"@hashgraph/proto@npm:2.10.0": - version: 2.10.0 - resolution: "@hashgraph/proto@npm:2.10.0" +"@hashgraph/proto@npm:2.11.0": + version: 2.11.0 + resolution: "@hashgraph/proto@npm:2.11.0" dependencies: long: ^4.0.0 protobufjs: ^6.11.3 - checksum: 0e94246a6c9fd7bafde9f9f86f0a81a473493ff15742cc3978108c906439ae2ab2e4b38f1af1895286d07974dea5e94193919e5b882d9b37f97abf65aa2e74d3 + checksum: c05cd2d29b6c17ce8a3e1066c68d217df7a36c2d812b0176344364b91c1f46d8e8a7a384a944fc1a56af2e8297e1e7f013cb992fe00bb5bac7091bc34d23065e languageName: node linkType: hard -"@hashgraph/sdk@npm:^2.18.3": - version: 2.18.3 - resolution: "@hashgraph/sdk@npm:2.18.3" +"@hashgraph/sdk@npm:^2.19.0": + version: 2.19.0 + resolution: "@hashgraph/sdk@npm:2.19.0" dependencies: "@ethersproject/rlp": ^5.7.0 "@grpc/grpc-js": ^1.7.0 "@hashgraph/cryptography": ^1.4.1 - "@hashgraph/proto": 2.10.0 + "@hashgraph/proto": 2.11.0 axios: ^0.27.2 bignumber.js: ^9.1.0 crypto-js: ^4.1.1 @@ -277,7 +277,7 @@ __metadata: peerDependenciesMeta: expo: optional: true - checksum: 8970f19ccb5f66ce7ff2a25d34c83d7d9a98b49915e5c1a8336d74d4bf6a5f8483f08cc798c79896c7794eff32436da82ea4fc15ea30c19cb1ef2b7b236fd7fa + checksum: a584925f827acab8b07bd5e6fb814181d120629867d8a7ae793cfad7cf4b18b6726bf0ae4371dfcec621dec2d8f5cd0e0c3cef8a3a0927357c200891679aaaf6 languageName: node linkType: hard @@ -1861,8 +1861,8 @@ __metadata: version: 0.0.0-use.local resolution: "api-gateway@workspace:api-gateway" dependencies: - "@guardian/common": ^2.7.0 - "@guardian/interfaces": ^2.7.0 + "@guardian/common": ^2.8.0-prerelease + "@guardian/interfaces": ^2.8.0-prerelease "@types/express": ^4.17.13 "@types/express-fileupload": ^1.4.1 "@types/jszip": ^3.4.1 @@ -2042,8 +2042,8 @@ __metadata: version: 0.0.0-use.local resolution: "auth-service@workspace:auth-service" dependencies: - "@guardian/common": ^2.7.0 - "@guardian/interfaces": ^2.7.0 + "@guardian/common": ^2.8.0-prerelease + "@guardian/interfaces": ^2.8.0-prerelease "@mikro-orm/core": ~5.3.0 "@mikro-orm/mongodb": ~5.3.0 "@types/jsonwebtoken": ^8.5.4 @@ -4485,9 +4485,9 @@ __metadata: version: 0.0.0-use.local resolution: "guardian-service@workspace:guardian-service" dependencies: - "@guardian/common": ^2.7.0 - "@guardian/interfaces": ^2.7.0 - "@hashgraph/sdk": ^2.18.3 + "@guardian/common": ^2.8.0-prerelease + "@guardian/interfaces": ^2.8.0-prerelease + "@hashgraph/sdk": ^2.19.0 "@mikro-orm/core": ~5.3.0 "@mikro-orm/mongodb": ~5.3.0 "@transmute/credentials-context": ^0.7.0-unstable.60 @@ -6433,8 +6433,8 @@ __metadata: version: 0.0.0-use.local resolution: "logger-service@workspace:logger-service" dependencies: - "@guardian/common": ^2.7.0 - "@guardian/interfaces": ^2.7.0 + "@guardian/common": ^2.8.0-prerelease + "@guardian/interfaces": ^2.8.0-prerelease "@mikro-orm/core": ~5.3.0 "@mikro-orm/mongodb": ~5.3.0 "@types/fs-extra": ^9.0.12 @@ -7014,7 +7014,7 @@ __metadata: version: 0.0.0-use.local resolution: "mrv-sender@workspace:mrv-sender" dependencies: - "@guardian/common": ^2.7.0 + "@guardian/common": ^2.8.0-prerelease "@transmute/credentials-context": 0.7.0-unstable.40 "@transmute/did-context": 0.7.0-unstable.40 "@transmute/ed25519-signature-2018": 0.7.0-unstable.40 @@ -10172,9 +10172,9 @@ __metadata: version: 0.0.0-use.local resolution: "worker-service@workspace:worker-service" dependencies: - "@guardian/common": ^2.7.0 - "@guardian/interfaces": ^2.7.0 - "@hashgraph/sdk": ^2.18.3 + "@guardian/common": ^2.8.0-prerelease + "@guardian/interfaces": ^2.8.0-prerelease + "@hashgraph/sdk": ^2.19.0 "@transmute/credentials-context": ^0.7.0-unstable.60 "@transmute/did-context": ^0.7.0-unstable.60 "@transmute/ed25519-signature-2018": ^0.7.0-unstable.60