diff --git a/.deploy/SSL2.yaml b/.deploy/SSL2.yaml new file mode 100644 index 00000000..7c48333d --- /dev/null +++ b/.deploy/SSL2.yaml @@ -0,0 +1,11 @@ +# secret.yaml +apiVersion: v1 +kind: Secret + +metadata: + name: entity-ssl-test + namespace: hypermine-development +type: kubernetes.io/tls +stringData: + tls.key: "" + tls.crt: "" diff --git a/.deploy/cert2.yaml b/.deploy/cert2.yaml new file mode 100644 index 00000000..228068b0 --- /dev/null +++ b/.deploy/cert2.yaml @@ -0,0 +1,13 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: entity-ssl + namespace: hypermine-development +spec: + dnsNames: + - api.entity-test.hypersign.id + - "*.api.entity-test.hypersign.id" + issuerRef: + kind: Issuer + name: letsencrypt-production + secretName: entity-ssl-test diff --git a/.deploy/deployment2.yaml b/.deploy/deployment2.yaml new file mode 100644 index 00000000..c58387ec --- /dev/null +++ b/.deploy/deployment2.yaml @@ -0,0 +1,137 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: entity-api-test + namespace: hypermine-development +spec: + replicas: 1 + + selector: + matchLabels: + app: entity-api-test + template: + metadata: + labels: + app: entity-api-test + spec: + containers: + - name: entity-api-test + image: __GOOGLE_ARTIFACT_URL__/__GOOGLE_PROJECT_ID__/__GOOGLE_ARTIFACT_REPO__/enity-api-test-service:__LATEST_RELEASE_TAG__ + imagePullPolicy: Always + livenessProbe: + httpGet: + path: /api/v1/edv/state + port: 3001 + initialDelaySeconds: 120 + periodSeconds: 30 + failureThreshold: 3 + + resources: + limits: + memory: "1Gi" + cpu: "500m" + ports: + - containerPort: __PORT__ + env: + - name: PORT + value: "__PORT__" + - name: BASE_DB_PATH + value: __BASE_DB_PATH__ + - name: DB_CONFIG + value: __DB_CONFIG__ + - name: HID_NETWORK_RPC + value: __HID_NETWORK_RPC__ + - name: HID_NETWORK_API + value: __HID_NETWORK_API__ + - name: HID_NETWORK_NAMESPACE + value: __HID_NETWORK_NAMESPACE__ + - name: EDV_BASE_URL + value: __EDV_BASE_URL__ + - name: EDV_CONFIG_DIR + value: __EDV_CONFIG_DIR__ + - name: EDV_DID_FILE_PATH + value: __EDV_DID_FILE_PATH__ + - name: EDV_KEY_FILE_PATH + value: __EDV_KEY_FILE_PATH__ + - name: MNEMONIC + value: __MNEMONIC__ + - name: JWT_SECRET + value: __JWT_SECRET__ + - name: GLOBAL_TXN_CONTROLLER_QUEUE + value: __GLOBAL_TXN_CONTROLLER_QUEUE__ + - name: RABBIT_MQ_URI + value: __RABBIT_MQ_URI__ + - name: WHITELISTED_CORS + value: "['https://entity.hypersign.id','https://api.entity.hypersign.id','https://wallet-prajna.hypersign.id']" + volumeMounts: + - name: mongo + mountPath: "/data" + volumes: + - name: mongo + secret: + secretName: mongo +--- +apiVersion: v1 +kind: Service +metadata: + name: entity-api-test-service + namespace: hypermine-development + annotations: + cloud.google.com/neg: '{"ingress": true}' + +spec: + type: NodePort + selector: + app: entity-api-test + ports: + - port: __PORT__ + targetPort: __PORT__ + protocol: TCP +# --- +# apiVersion: apps/v1 +# kind: Deployment +# metadata: +# name: nginx-entity +# namespace: hypermine-development +# spec: +# replicas: 1 + +# selector: +# matchLabels: +# app: nginx-entity +# template: +# metadata: +# labels: +# app: nginx-entity +# spec: +# containers: +# - name: nginx-entity +# image: __GOOGLE_ARTIFACT_URL__/__GOOGLE_PROJECT_ID__/__GOOGLE_ARTIFACT_REPO__/nginx-entity:latest +# imagePullPolicy: Always + +# resources: +# limits: +# memory: "256m" +# cpu: "500m" +# ports: +# - containerPort: 8080 +# --- +# apiVersion: v1 +# kind: Service +# metadata: +# name: nginx-entity-service +# namespace: hypermine-development +# annotations: +# cloud.google.com/neg: '{"ingress": true}' + +# spec: +# type: NodePort +# selector: +# app: nginx-entity +# ports: +# - port: 8080 +# targetPort: 8080 +# protocol: TCP + + + diff --git a/.deploy/ingress2.yaml b/.deploy/ingress2.yaml new file mode 100644 index 00000000..4279fbb9 --- /dev/null +++ b/.deploy/ingress2.yaml @@ -0,0 +1,120 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: entity-ingress + namespace: hypermine-development + annotations: + kubernetes.io/ingress.allow-http: "false" + kubernetes.io/force-ssl-redirect: redirect + ingressClassName: "gce" + kubernetes.io/ingress.global-static-ip-name: entity-ip + cert-manager.io/issuer: letsencrypt-production + labels: + name: ingress + +spec: + tls: + - secretName: entity-ssl + hosts: + - "api.entity.hypersign.id" + - "*.api.entity.hypersign.id" + - "api.entity-test.hypersign.id" + - "*.api.entity-test.hypersign.id" + + rules: + - host: "api.entity.hypersign.id" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: entity-api-service + port: + number: 3001 + - path: /ssi/ + pathType: Prefix + backend: + service: + name: entity-api-service + port: + number: 3001 + - path: /api/ + pathType: Prefix + backend: + service: + name: entity-api-service + port: + number: 3001 + - host: "*.api.entity.hypersign.id" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: entity-api-service + port: + number: 3001 + - path: /ssi/ + pathType: Prefix + backend: + service: + name: entity-api-service + port: + number: 3001 + - path: /api/ + pathType: Prefix + backend: + service: + name: entity-api-service + port: + number: 3001 + - host: "api.entity-test.hypersign.id" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: entity-api-test-service + port: + number: 3001 + - path: /ssi/ + pathType: Prefix + backend: + service: + name: entity-api-test-service + port: + number: 3001 + - path: /api/ + pathType: Prefix + backend: + service: + name: entity-api-test-service + port: + number: 3001 + - host: "*.api.entity-test.hypersign.id" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: entity-api-test-service + port: + number: 3001 + - path: /ssi/ + pathType: Prefix + backend: + service: + name: entity-api-test-service + port: + number: 3001 + - path: /api/ + pathType: Prefix + backend: + service: + name: entity-api-test-service + port: + number: 3001 diff --git a/.github/workflows/CI-CD.yaml b/.github/workflows/CI-CD.yaml index a0c3ce29..c762e896 100644 --- a/.github/workflows/CI-CD.yaml +++ b/.github/workflows/CI-CD.yaml @@ -45,9 +45,9 @@ jobs: GOOGLE_ARTIFACT_URL: ${{secrets.GOOGLE_ARTIFACT_URL}} GOOGLE_ARTIFACT_REPO: ${{secrets.GOOGLE_ARTIFACT_REPO}} run: - docker build -t $GOOGLE_ARTIFACT_URL/$GOOGLE_PROJECT_ID/$GOOGLE_ARTIFACT_REPO/enity-api-service:${{ env.LATEST_RELEASE_TAG }} . + docker build -t $GOOGLE_ARTIFACT_URL/$GOOGLE_PROJECT_ID/$GOOGLE_ARTIFACT_REPO/enity-api-test-service:${{ env.LATEST_RELEASE_TAG }} . - docker push $GOOGLE_ARTIFACT_URL/$GOOGLE_PROJECT_ID/$GOOGLE_ARTIFACT_REPO/enity-api-service:${{ env.LATEST_RELEASE_TAG }} + docker push $GOOGLE_ARTIFACT_URL/$GOOGLE_PROJECT_ID/$GOOGLE_ARTIFACT_REPO/enity-api-test-service:${{ env.LATEST_RELEASE_TAG }} - name: "Docker Build and Push" env: GOOGLE_PROJECT_ID: ${{secrets.GOOGLE_PROJECT_ID}} @@ -103,6 +103,10 @@ jobs: run: find .deploy/deployment.yaml -type f -exec sed -i -e "s#__EDV_DID_FILE_PATH__#${{ secrets.EDV_DID_FILE_PATH }}#" {} \; - name: "Replace secrets" run: find .deploy/deployment.yaml -type f -exec sed -i -e "s#__EDV_KEY_FILE_PATH__#${{ secrets.EDV_KEY_FILE_PATH }}#" {} \; + - name: "Replace secrets" + run: find .deploy/deployment.yaml -type f -exec sed -i -e "s#__GLOBAL_TXN_CONTROLLER_QUEUE__#${{ secrets.GLOBAL_TXN_CONTROLLER_QUEUE }}#" {} \; + - name: "Replace secrets" + run: find .deploy/deployment.yaml -type f -exec sed -i -e "s#__RABBIT_MQ_URI__#${{ secrets.RABBIT_MQ_URI }}#" {} \; - name: "Replace secrets" run: find .deploy/deployment.yaml -type f -exec sed -i -e "s#__MNEMONIC__#${{ secrets.MNEMONIC }}#" {} \; - name: "Replace secrets" @@ -114,4 +118,4 @@ jobs: - name: "Replace secrets" run: find .deploy/deployment.yaml -type f -exec sed -i ''s/__GOOGLE_PROJECT_ID__/${{ secrets.GOOGLE_PROJECT_ID }}/g'' {} \; - name: "Deploy to GKE" - run: kubectl apply -f .deploy/deployment.yaml + run: kubectl apply -f .deploy/deployment2.yaml diff --git a/package.json b/package.json index f7da6645..1778a7d8 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,8 @@ "@nestjs/passport": "^9.0.0", "@nestjs/platform-express": "^9.0.0", "@nestjs/swagger": "^6.1.4", + "amqp-connection-manager": "^4.1.14", + "amqplib": "^0.10.4", "argon2": "^0.30.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", @@ -42,7 +44,7 @@ "fs": "^0.0.1-security", "hid-hd-wallet": "git+https://github.com/hypersign-protocol/hid-hd-wallet.git#main", "hs-ssi-sdk": "github:hypersign-protocol/hid-ssi-js-sdk#testcase/bjj", - "hypersign-edv-client": "github:hypersign-protocol/hypersign-edv-client#develop", + "hypersign-edv-client": "github:hypersign-protocol/hypersign-edv-client#deleteByDocumentId", "idb-keyval": "^6.2.1", "mongoose": "^6.8.3", "passport": "^0.6.0", diff --git a/src/app.module.ts b/src/app.module.ts index e996bd16..bb649722 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -7,6 +7,7 @@ import { DidModule } from './did/did.module'; import { SchemaModule } from './schema/schema.module'; import { CredentialModule } from './credential/credential.module'; import { PresentationModule } from './presentation/presentation.module'; +import { TxSendModuleModule } from './tx-send-module/tx-send-module.module'; @Module({ imports: [ ConfigModule.forRoot({ @@ -18,6 +19,7 @@ import { PresentationModule } from './presentation/presentation.module'; SchemaModule, CredentialModule, PresentationModule, + TxSendModuleModule, ], controllers: [], providers: [{ provide: APP_FILTER, useClass: AllExceptionsFilter }], diff --git a/src/did/did.module.ts b/src/did/did.module.ts index f029853b..588b5d41 100644 --- a/src/did/did.module.ts +++ b/src/did/did.module.ts @@ -23,8 +23,9 @@ import { TrimMiddleware } from 'src/utils/middleware/trim.middleware'; import { databaseProviders } from '../mongoose/tenant-mongoose-connections'; import { didProviders } from './providers/did.provider'; import { JwtStrategy } from '../utils/jwt.strategy'; +import { TxSendModuleModule } from 'src/tx-send-module/tx-send-module.module'; @Module({ - imports: [EdvModule, HidWalletModule], + imports: [EdvModule, HidWalletModule, TxSendModuleModule], controllers: [DidController], providers: [ JwtStrategy, diff --git a/src/did/services/did.service.ts b/src/did/services/did.service.ts index 15f4d06f..03dd9ace 100644 --- a/src/did/services/did.service.ts +++ b/src/did/services/did.service.ts @@ -30,6 +30,7 @@ import { Did as IDidDto } from '../schemas/did.schema'; import { AddVerificationMethodDto } from '../dto/addVm.dto'; import { getAppVault, getAppMenemonic } from '../../utils/app-vault-service'; import { ConfigService } from '@nestjs/config'; +import { TxSendModuleService } from 'src/tx-send-module/tx-send-module.service'; @Injectable({ scope: Scope.REQUEST }) export class DidService { @@ -39,6 +40,7 @@ export class DidService { private readonly hidWallet: HidWalletService, private readonly didSSIService: DidSSIService, private readonly config: ConfigService, + private readonly txnService: TxSendModuleService, ) {} // TODO: need to fix this once ed25519 is finished. @@ -376,7 +378,20 @@ export class DidService { 'register() method: before calling hypersignDid.register ', 'DidService', ); - registerDidDoc = await hypersignDid.register(params); + + const signInfos = await hypersignDid.createSignInfos({ + didDocument, + privateKeyMultibase, + verificationMethodId: verificationMethodId, + }); + await this.txnService.sendDIDTxn( + didDocument, + signInfos, + verificationMethodId, + appMenemonic, + ); + + // registerDidDoc = await hypersignDid.register(params); data = await this.didRepositiory.findOneAndUpdate( { did: didDocument['id'] }, { diff --git a/src/hid-wallet/hid-wallet.module.ts b/src/hid-wallet/hid-wallet.module.ts index 17903d22..f0afa284 100644 --- a/src/hid-wallet/hid-wallet.module.ts +++ b/src/hid-wallet/hid-wallet.module.ts @@ -3,5 +3,6 @@ import { HidWalletService } from './services/hid-wallet.service'; @Module({ controllers: [], providers: [HidWalletService], + exports: [HidWalletService], }) export class HidWalletModule {} diff --git a/src/hid-wallet/services/hid-wallet.service.ts b/src/hid-wallet/services/hid-wallet.service.ts index e5a74e64..33b1d024 100644 --- a/src/hid-wallet/services/hid-wallet.service.ts +++ b/src/hid-wallet/services/hid-wallet.service.ts @@ -11,6 +11,7 @@ export class HidWalletService { async generateWallet(mnemonic?: string): Promise<{ mnemonic: string; + wallet: DirectSecp256k1HdWallet; address: string; }> { Logger.log('generateWallet() method: starts....', 'generateWallet'); @@ -41,6 +42,7 @@ export class HidWalletService { this.mnemonic = generatedMnemonice; return { mnemonic: generatedMnemonice, + wallet: wallet, address: hidWalletAddress[0].address, }; } diff --git a/src/schema/schema.module.ts b/src/schema/schema.module.ts index abaecc11..65e2bef2 100644 --- a/src/schema/schema.module.ts +++ b/src/schema/schema.module.ts @@ -15,9 +15,10 @@ import { WhitelistSSICorsMiddleware } from 'src/utils/middleware/cors.middleware import { TrimMiddleware } from 'src/utils/middleware/trim.middleware'; import { schemaProviders } from './providers/schema.provider'; import { databaseProviders } from '../mongoose/tenant-mongoose-connections'; +import { TxSendModuleModule } from 'src/tx-send-module/tx-send-module.module'; @Module({ - imports: [DidModule], + imports: [DidModule, TxSendModuleModule], controllers: [SchemaController], providers: [ SchemaService, diff --git a/src/tx-send-module/tx-send-module.module.ts b/src/tx-send-module/tx-send-module.module.ts new file mode 100644 index 00000000..eda3628a --- /dev/null +++ b/src/tx-send-module/tx-send-module.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { TxSendModuleService } from './tx-send-module.service'; +import { ConfigModule } from '@nestjs/config'; +import { HidWalletModule } from 'src/hid-wallet/hid-wallet.module'; +import { DidSSIService } from 'src/did/services/did.ssi.service'; + +@Module({ + imports: [ConfigModule, HidWalletModule], + controllers: [], + providers: [TxSendModuleService, DidSSIService], + exports: [TxSendModuleService], +}) +export class TxSendModuleModule {} diff --git a/src/tx-send-module/tx-send-module.service.spec.ts b/src/tx-send-module/tx-send-module.service.spec.ts new file mode 100644 index 00000000..d2bca434 --- /dev/null +++ b/src/tx-send-module/tx-send-module.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { TxSendModuleService } from './tx-send-module.service'; + +describe('TxSendModuleService', () => { + let service: TxSendModuleService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [TxSendModuleService], + }).compile(); + + service = module.get(TxSendModuleService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/tx-send-module/tx-send-module.service.ts b/src/tx-send-module/tx-send-module.service.ts new file mode 100644 index 00000000..8880801c --- /dev/null +++ b/src/tx-send-module/tx-send-module.service.ts @@ -0,0 +1,155 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import amqp, { ChannelWrapper } from 'amqp-connection-manager'; +import { MsgRegisterDID } from 'hs-ssi-sdk/build/libs/generated/ssi/tx'; +import { DidSSIService } from 'src/did/services/did.ssi.service'; +import { HidWalletService } from 'src/hid-wallet/services/hid-wallet.service'; +import { StdFee } from '@cosmjs/stargate'; +import { + MsgExec, + MsgGrant, + MsgRevoke, +} from 'cosmjs-types/cosmos/authz/v1beta1/tx'; + +@Injectable() +export class TxSendModuleService { + private channel: ChannelWrapper; + + constructor( + private readonly configService: ConfigService, + private readonly hidWalletService: HidWalletService, + private readonly didSSIService: DidSSIService, + ) { + this.connect(); + } + + async prepareMsgCreateDID( + didDocument, + didDocumentSigned, + verificationMethodId, + txAuthor, + ): Promise { + const proof = didDocumentSigned?.find((e) => { + return e.verification_method_id === verificationMethodId; + }); + + const vm = didDocument.verificationMethod?.find((e) => { + return e.id == verificationMethodId; + }); + let signatureType = ''; + let proofPurpose = ''; + switch (vm.type) { + case 'Ed25519VerificationKey2020': { + signatureType = 'Ed25519Signature2020'; + proofPurpose = 'assertionMethod'; + + break; + } + default: { + throw Error('Type is not matched'); + } + } + + return MsgRegisterDID.fromPartial({ + didDocument: didDocument, + didDocumentProofs: [ + { + verificationMethod: verificationMethodId, + type: signatureType, + proofPurpose: proofPurpose, + created: proof.created, + proofValue: proof.signature, + }, + ], + txAuthor: txAuthor, + }); + } + + async connect() { + Logger.log('Connecting Rabbit'); + const connection = await amqp.connect( + this.configService.get('RABBIT_MQ_URI'), + ); + this.channel = await connection.createChannel(); + Logger.log('Connected Rabbit'); + } + + async sendDIDTxn( + didDocument, + didDocumentSigned, + verificationMethodId, + granteeMnemonic, + ) { + if (!this.channel) { + await this.connect(); + } + + const { wallet, address } = await this.hidWalletService.generateWallet( + granteeMnemonic, + ); + const msgCreateDID = await this.prepareMsgCreateDID( + didDocument, + didDocumentSigned, + verificationMethodId, + address, + ); + + const { address: granterAddress } = + await this.hidWalletService.generateWallet( + this.configService.get('MNEMONIC'), + ); + + const authExecMsg: MsgExec = { + grantee: address, + msgs: [ + { + typeUrl: '/hypersign.ssi.v1.MsgRegisterDID', + value: MsgRegisterDID.encode(msgCreateDID).finish(), + }, + ], + }; + const fee = { + amount: [ + { + denom: 'uhid', + amount: '100', + }, + ], + gas: '500000', + granter: granterAddress, // NOTE: It is VERY IMPORTANT to explicitly pass granter's address + }; + const txMsg = { + typeUrl: '/cosmos.authz.v1beta1.MsgExec', + value: authExecMsg, + }; + const queue = 'DID_TXN_QUEUE_' + address; + await this.channel.assertQueue(queue, { + durable: false, + }); + + const sendToQueue1 = await this.channel.sendToQueue( + queue, + Buffer.from(JSON.stringify(txMsg)), + ); + + const podENV = { + RMQ_URL: this.configService.get('RABBIT_MQ_URI'), + DID_QUEUE_NAME: 'DID_TXN_QUEUE_' + address, + NODE_RPC_URL: this.configService.get('HID_NETWORK_RPC'), + GRANTEE_MNEMONIC: granteeMnemonic, + GRANTER_ADDRESS: granterAddress, + FIXED_FEE: '50', + ESTIMATE_GAS_PRICE: '155303', + podName: 'did-txn-dynamic', + }; + + await this.channel.assertQueue('GLOBAL_TXN_CONTROLLER_QUEUE', { + durable: false, + }); + const sendToQueue2 = await this.channel.sendToQueue( + this.configService.get('GLOBAL_TXN_CONTROLLER_QUEUE'), + Buffer.from(JSON.stringify(podENV)), + ); + console.log(sendToQueue1, sendToQueue2); + } +}