diff --git a/src/controller/credential_controller.ts b/src/controller/credential_controller.ts index 72fbe5e..72a438c 100644 --- a/src/controller/credential_controller.ts +++ b/src/controller/credential_controller.ts @@ -22,9 +22,9 @@ const { CHAIN_SPACE_ID, CHAIN_SPACE_AUTH } = process.env; export async function issueVC(req: express.Request, res: express.Response) { let data = req.body; const api = Cord.ConfigService.get('api'); - if (!authorIdentity) { - await addDelegateAsRegistryDelegate(); - } + // if (!authorIdentity) { + // await addDelegateAsRegistryDelegate(); + // } try { const validationError = validateCredential(data); @@ -40,11 +40,12 @@ export async function issueVC(req: express.Request, res: express.Response) { const parsedSchema = JSON.parse(schema?.cordSchema as string); - let holder = issuerDid.uri; + let holder = delegateDid.uri; if (data.properties.id) { holder = data.properties.id; delete data.properties.id; } + const newCredContent = await Vc.buildVcFromContent( parsedSchema.schema, data.properties, @@ -61,8 +62,9 @@ export async function issueVC(req: express.Request, res: express.Response) { async (data) => ({ signature: await issuerKeysProperty.assertionMethod.sign(data), keyType: issuerKeysProperty.assertionMethod.type, - keyUri: `${issuerDid.uri}${issuerDid.assertionMethod![0].id - }` as Cord.DidResourceUri, + keyUri: `${issuerDid.uri}${ + issuerDid.assertionMethod![0].id + }` as Cord.DidResourceUri, }), issuerDid, api, @@ -101,9 +103,9 @@ export async function issueVC(req: express.Request, res: express.Response) { if (statement) { await dataSource.manager.save(cred); - return res - .status(200) - .json({ result: 'success', identifier: cred.identifier, vc: vc }); + return res.status(200).json({ + result: { message: 'SUCCESS', identifier: cred.identifier, vc }, + }); } else { return res.status(400).json({ error: 'Credential not issued' }); } @@ -191,8 +193,9 @@ export async function updateCred(req: express.Request, res: express.Response) { async (data) => ({ signature: await issuerKeysProperty.assertionMethod.sign(data), keyType: issuerKeysProperty.assertionMethod.type, - keyUri: `${issuerDid.uri}${issuerDid.assertionMethod![0].id - }` as Cord.DidResourceUri, + keyUri: `${issuerDid.uri}${ + issuerDid.assertionMethod![0].id + }` as Cord.DidResourceUri, }), issuerDid, api, diff --git a/src/controller/did_controller.ts b/src/controller/did_controller.ts index eac7642..43046bd 100644 --- a/src/controller/did_controller.ts +++ b/src/controller/did_controller.ts @@ -1,34 +1,73 @@ import * as Cord from '@cord.network/sdk'; import express from 'express'; import 'reflect-metadata'; -import { processServiceData } from '../utils/DidValidationUtils' -import { mnemonicGenerate } from '@polkadot/util-crypto'; -import { - addDelegateAsRegistryDelegate, - authorIdentity, - createDid -} from '../init'; +import { createDid } from '../init'; +import { studio_encrypt } from '../identity/org'; -export async function generateDid( +export async function generateDid(req: express.Request, res: express.Response) { + const { didName } = req.body; + + try { + const { mnemonic, document } = await createDid(didName); + + return res.status(200).json({ + result: { + message: 'Successfully created did', + mnemonic, + document, + }, + }); + } catch (error) { + console.log('err: ', error); + return res.status(400).json({ error: 'Did not created' }); + } +} + +export async function didNameNewCheck( req: express.Request, res: express.Response ) { + const id = req.params.id; + const api = Cord.ConfigService.get('api'); try { - if (!authorIdentity) { - await addDelegateAsRegistryDelegate(); - } - const serviceData = req.body.services[0]; - const processedService = processServiceData(serviceData); - const { mnemonic, delegateKeys, document } = await createDid(authorIdentity, processedService); - - return res.status(200).json({ mnemonic, delegateKeys, document }); + const encodedDidNameOwner = await api.call.didApi.queryByName(id); + + // Check if the DID has a linked URI + const hasUri = encodedDidNameOwner?.isSome + ? Boolean( + Cord.Did.linkedInfoFromChain(encodedDidNameOwner)?.document?.uri + ) + : false; + + return res.status(200).json({ result: hasUri }); } catch (error) { - console.log('err: ', error); - return res.status(500).json({ error: 'Did not created' }); + console.error('Error querying DID name:', error); + return res + .status(400) + .json({ success: false, message: 'Internal server error' }); } } +export async function encryptMnemonic( + req: express.Request, + res: express.Response +) { + try { + const { issuerMnemonic } = req.body; + const encryptedMnemonic = JSON.stringify( + await studio_encrypt(issuerMnemonic) + ); + return res.status(200).json({ + result: { message: 'Encryption Successfully', encryptedMnemonic }, + }); + } catch (error) { + console.error('Error in encryption', error); + return res + .status(400) + .json({ success: false, message: 'Internal server error' }); + } +} diff --git a/src/controller/schema_controller.ts b/src/controller/schema_controller.ts index 7bae0ed..2cc1dc6 100644 --- a/src/controller/schema_controller.ts +++ b/src/controller/schema_controller.ts @@ -20,9 +20,9 @@ export async function createSchema( res: express.Response ) { try { - if (!authorIdentity) { - await addDelegateAsRegistryDelegate(); - } + // if (!authorIdentity) { + // await addDelegateAsRegistryDelegate(); + // } let data = req.body.schema?.schema || req.body.schema || null; @@ -70,8 +70,7 @@ export async function createSchema( await dataSource.manager.save(schemaData); return res.status(200).json({ - result: 'SUCCESS', - schemaId: schemaData.identifier, + result: { message: 'SUCCESS', schemaId: schemaData.identifier }, }); } return res.status(400).json({ error: 'SchemaDetails not created' }); diff --git a/src/identity/org.ts b/src/identity/org.ts new file mode 100644 index 0000000..46950af --- /dev/null +++ b/src/identity/org.ts @@ -0,0 +1,99 @@ +import { cryptoWaitReady } from '@polkadot/util-crypto'; +import { Keyring } from '@polkadot/keyring'; +import { KeyringInstance, KeyringPair } from '@polkadot/keyring/types'; +import { + mnemonicToMiniSecret, + mnemonicGenerate, + naclDecrypt, + naclEncrypt, +} from '@polkadot/util-crypto'; +import { stringToU8a, u8aToString, u8aToHex, hexToU8a } from '@polkadot/util'; +import nacl, { BoxKeyPair } from 'tweetnacl'; + +export type Identity = { + key: KeyringPair; + boxPair: BoxKeyPair; +}; + +export type EncryptedString = { + encrypt: string; + nonce: string; +}; + +let studio_identity: Identity; + +let keyring: KeyringInstance; + +async function keyringInit() { + await cryptoWaitReady(); + + keyring = new Keyring({ type: 'sr25519', ss58Format: 29 }); +} + +async function generateNewKey(phrase: string) { + if (!keyring) { + await keyringInit(); + } + const seed = mnemonicToMiniSecret(phrase); + return { + key: keyring.addFromSeed(seed), + boxPair: nacl.box.keyPair.fromSecretKey(seed), + }; +} + +export async function studio_identity_init(mnemonic: string) { + studio_identity = await generateNewKey(mnemonic); +} + +export async function org_identity_create() { + const mnemonic = mnemonicGenerate(); + const org: [string, Identity] = [mnemonic, await generateNewKey(mnemonic)]; + return org; +} + +export async function encrypt(key: Identity, u8data: Buffer) { + //const u8data = stringToU8a(data); + const { encrypted, nonce } = naclEncrypt(u8data, key.boxPair.secretKey); + return { encrypt: u8aToHex(encrypted), nonce: u8aToHex(nonce) }; +} + +export async function decrypt(key: Identity, encrypted: EncryptedString) { + const decrypt = naclDecrypt( + hexToU8a(encrypted.encrypt), + hexToU8a(encrypted.nonce), + key.boxPair.secretKey + ); + return decrypt; +} + +export async function studio_encrypt(mnemonic: string) { + const u8data = stringToU8a(mnemonic); + const { encrypted, nonce } = naclEncrypt( + u8data, + studio_identity.boxPair.secretKey + ); + return { encrypt: u8aToHex(encrypted), nonce: u8aToHex(nonce) }; +} + +export async function studio_decrypt(encrypted: EncryptedString) { + const decrypt = naclDecrypt( + hexToU8a(encrypted.encrypt), + hexToU8a(encrypted.nonce), + studio_identity.boxPair.secretKey + ); + return u8aToString(decrypt); +} + +export async function org_identity_from_mnemonic(mnemonic: string) { + return await generateNewKey(mnemonic); +} + +export async function org_identity_from_encrypted_mnemonic( + encrypted: EncryptedString +) { + const mnemonic = await studio_decrypt(encrypted); + if (mnemonic) { + return await generateNewKey(mnemonic); + } + return null; +} diff --git a/src/index.ts b/src/index.ts index 0699e1a..2022975 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,9 @@ import express from 'express'; +import cluster from 'node:cluster'; +import os from 'node:os'; + import { dataSource } from './dbconfig'; -import { addDelegateAsRegistryDelegate } from './init'; +import { addDelegateAsRegistryDelegate, checkDidAndIdentities } from './init'; import { createSchema, getSchemaById } from './controller/schema_controller'; import { documentHashOnChain, @@ -9,68 +12,98 @@ import { revokeCred, updateCred, } from './controller/credential_controller'; -import { generateDid } from './controller/did_controller'; +import { + didNameNewCheck, + encryptMnemonic, + generateDid, +} from './controller/did_controller'; import app from './server'; +import { studio_identity_init } from './identity/org'; -const { PORT } = process.env; +const { PORT, MNEMONIC } = process.env; -const credentialRouter = express.Router({ mergeParams: true }); -const schemaRouter = express.Router({ mergeParams: true }); -const didRouter = express.Router({ mergeParams: true }); +const numCPUs = os.cpus().length; -credentialRouter.post('/', async (req, res) => { - return await issueVC(req, res); -}); +if (cluster.isMaster) { + console.log(`Primary ${process.pid} is running`); -credentialRouter.get('/:id', async (req, res) => { - return await getCredById(req, res); -}); + for (let i = 0; i < numCPUs; i++) { + cluster.fork(); + } + cluster.on('exit', (worker, code, signal) => { + console.log(`worker ${worker.process.pid} died`); + cluster.fork(); + }); +} else { + const credentialRouter = express.Router({ mergeParams: true }); + const schemaRouter = express.Router({ mergeParams: true }); + const didRouter = express.Router({ mergeParams: true }); -credentialRouter.put('/update/:id', async (req, res) => { - return await updateCred(req, res); -}); + credentialRouter.post('/', async (req, res) => { + return await issueVC(req, res); + }); -credentialRouter.post('/revoke/:id', async (req, res) => { - return await revokeCred(req, res); -}); + credentialRouter.get('/:id', async (req, res) => { + return await getCredById(req, res); + }); -schemaRouter.post('/', async (req, res) => { - return await createSchema(req, res); -}); + credentialRouter.put('/update/:id', async (req, res) => { + return await updateCred(req, res); + }); -schemaRouter.get('/:id', async (req, res) => { - return await getSchemaById(req, res); -}); + credentialRouter.post('/revoke/:id', async (req, res) => { + return await revokeCred(req, res); + }); -didRouter.post('/create', async (req, res) => { - return await generateDid(req, res); -}) -app.use('/api/v1/schema', schemaRouter); -app.use('/api/v1/cred', credentialRouter); -app.use('/api/v1/did', didRouter); + schemaRouter.post('/', async (req, res) => { + return await createSchema(req, res); + }); -app.post('/api/v1/docHash', async (req, res) => { - return await documentHashOnChain(req, res); -}); + schemaRouter.get('/:id', async (req, res) => { + return await getSchemaById(req, res); + }); -app.get('/*', async (req, res) => { - return res.json({ - message: 'check https://docs.dhiway.com/api for details of the APIs', + didRouter.get('/didName/:id', async (req, res) => { + return await didNameNewCheck(req, res); }); -}); - -async function main() { - try { - await dataSource.initialize(); - await addDelegateAsRegistryDelegate(); - } catch (error) { - console.log('error: ', error); - throw new Error('Main error'); - } - app.listen(PORT, () => { - console.log(`Dhiway gateway is running at http://localhost:${PORT}`); + didRouter.post('/create', async (req, res) => { + return await generateDid(req, res); }); -} -main().catch((e) => console.log(e)); + didRouter.post('/encrypt', async (req, res) => { + return await encryptMnemonic(req, res); + }); + + app.use('/api/v1/schema', schemaRouter); + app.use('/api/v1/cred', credentialRouter); + app.use('/api/v1/did', didRouter); + + app.post('/api/v1/docHash', async (req, res) => { + return await documentHashOnChain(req, res); + }); + + app.get('/*', async (req, res) => { + return res.json({ + message: 'check https://docs.dhiway.com/api for details of the APIs', + }); + }); + + async function main() { + try { + await dataSource.initialize(); + await studio_identity_init(MNEMONIC as string); + // await addDelegateAsRegistryDelegate(); + await checkDidAndIdentities(MNEMONIC as string); + } catch (error) { + console.log('error: ', error); + throw new Error('Main error'); + } + + app.listen(PORT, () => { + console.log(`Dhiway gateway is running at http://localhost:${PORT}`); + }); + } + + main().catch((e) => console.log(e)); +} diff --git a/src/init.ts b/src/init.ts index dffe052..8fd0b2c 100644 --- a/src/init.ts +++ b/src/init.ts @@ -25,7 +25,8 @@ export async function createDidName( signCallback: Cord.SignExtrinsicCallback ): Promise { const api = Cord.ConfigService.get('api'); - const didNameClaimTx = await api.tx.didNames.register(name); + + const didNameClaimTx = await api.tx.didName.register(name); const authorizedDidNameClaimTx = await Cord.Did.authorizeTx( did, didNameClaimTx, @@ -33,16 +34,10 @@ export async function createDidName( submitterAccount.address ); await Cord.Chain.signAndSubmitTx(authorizedDidNameClaimTx, submitterAccount); - } -export async function createDid( - submitterAccount: Cord.CordKeyringPair, - service?: Cord.DidServiceEndpoint[], - didName?: string | undefined -): Promise<{ +export async function createDid(didName?: string | undefined): Promise<{ mnemonic: string; - delegateKeys: any; document: Cord.DidDocument; }> { try { @@ -66,7 +61,8 @@ export async function createDid( keyAgreement: [keyAgreement], assertionMethod: [assertionMethod], capabilityDelegation: [capabilityDelegation], - service: Array.isArray(service) && service.length > 0 ? service : [ + // Example service. + service: [ { id: '#my-service', type: ['service-type'], @@ -74,20 +70,20 @@ export async function createDid( }, ], }, - submitterAccount.address, + authorIdentity.address, async ({ data }) => ({ signature: authentication.sign(data), keyType: authentication.type, }) ); - await Cord.Chain.signAndSubmitTx(didCreationTx, submitterAccount); + await Cord.Chain.signAndSubmitTx(didCreationTx, authorIdentity); if (didName) { try { await createDidName( didUri, - submitterAccount, + authorIdentity, didName, async ({ data }) => ({ signature: authentication.sign(data), @@ -105,16 +101,17 @@ export async function createDid( if (!document) { throw new Error('DID was not successfully created.'); } + delegateDid = document; delegateKeysProperty = delegateKeys; - return { mnemonic, delegateKeys, document }; + + return { mnemonic, document }; } catch (err) { console.log('Error: ', err); throw new Error('Failed to create delegate DID'); } } - export async function checkDidAndIdentities(mnemonic: string): Promise { if (!mnemonic) return null; @@ -160,7 +157,7 @@ export async function addDelegateAsRegistryDelegate() { /* Creating delegate from authorIdentity. */ const { mnemonic: delegateMnemonic, document: delegateDid } = - await createDid(authorIdentity); + await createDid(); if (!document || !issuerKeys) { throw new Error('Failed to create DID');