diff --git a/src/core/common.ts b/src/core/common.ts new file mode 100644 index 00000000..eaa866d9 --- /dev/null +++ b/src/core/common.ts @@ -0,0 +1,59 @@ +import { ListOptions } from "../types/query-options"; +import lodash from "lodash"; +import { EncryptionMetadata } from "./service"; +import { EncryptedPayload } from "@akord/crypto/lib/types"; +import { base64ToArray } from "@akord/crypto"; + +export const handleListErrors = async (originalItems: Array, promises: Array>) + : Promise<{ items: Array, errors: Array<{ id: string, error: Error }> }> => { + const results = await Promise.all(promises.map(p => p.catch(e => e))); + const items = results.filter(result => !(result instanceof Error)); + const errors = results + .map((result, index) => ({ result, index })) + .filter((mapped) => mapped.result instanceof Error) + .map((filtered) => ({ id: (originalItems[filtered.index]).id, error: filtered.result })); + return { items, errors }; +} + +export const paginate = async (apiCall: any, listOptions: ListOptions & { vaultId?: string }): Promise> => { + let token = undefined; + let results = [] as T[]; + do { + const { items, nextToken } = await apiCall(listOptions); + results = results.concat(items); + token = nextToken; + listOptions.nextToken = nextToken; + if (nextToken === "null") { + token = undefined; + } + } while (token); + return results; +} + +export const mergeState = (currentState: any, stateUpdates: any): any => { + let newState = lodash.cloneDeepWith(currentState); + lodash.mergeWith( + newState, + stateUpdates, + function concatArrays(objValue, srcValue) { + if (lodash.isArray(objValue)) { + return objValue.concat(srcValue); + } + }); + return newState; +} + +export const getEncryptedPayload = (data: ArrayBuffer | string, metadata: EncryptionMetadata) + : EncryptedPayload => { + const { encryptedKey, iv } = metadata; + if (encryptedKey && iv) { + return { + encryptedKey, + encryptedData: { + iv: base64ToArray(iv), + ciphertext: data as ArrayBuffer + } + } + } + return null; +} diff --git a/src/core/membership.ts b/src/core/membership.ts index 46cd827c..3d11c5d5 100644 --- a/src/core/membership.ts +++ b/src/core/membership.ts @@ -1,6 +1,6 @@ import { actionRefs, objectType, status, functions, protocolTags } from "../constants"; import { v4 as uuidv4 } from "uuid"; -import { EncryptedKeys, Encrypter, deriveAddress, base64ToArray } from "@akord/crypto"; +import { EncryptedKeys, Encrypter, deriveAddress, base64ToArray, generateKeyPair, Keys } from "@akord/crypto"; import { Service } from "./service"; import { Membership, RoleType, StatusType } from "../types/membership"; import { GetOptions, ListOptions } from "../types/query-options"; @@ -8,6 +8,9 @@ import { MembershipInput, Tag, Tags } from "../types/contract"; import { Paginated } from "../types/paginated"; import { BadRequest } from "../errors/bad-request"; import { IncorrectEncryptionKey } from "../errors/incorrect-encryption-key"; +import { handleListErrors, paginate } from "./common"; +import { ProfileService } from "./profile"; +import { ProfileDetails } from "../types/profile-details"; export const activeStatus = [status.ACCEPTED, status.PENDING, status.INVITED] as StatusType[]; @@ -60,7 +63,7 @@ class MembershipService extends Service { .map(async (membershipProto: Membership) => { return await this.processMembership(membershipProto, !membershipProto.__public__ && listOptions.shouldDecrypt, membershipProto.__keys__); }) as Promise[]; - const { items, errors } = await this.handleListErrors(response.items, promises); + const { items, errors } = await handleListErrors(response.items, promises); return { items, nextToken: response.nextToken, @@ -77,7 +80,7 @@ class MembershipService extends Service { const list = async (options: ListOptions & { vaultId: string }) => { return await this.list(options.vaultId, options); } - return await this.paginate(list, { ...options, vaultId }); + return await paginate(list, { ...options, vaultId }); } /** @@ -196,7 +199,8 @@ class MembershipService extends Service { * @returns Promise with corresponding transaction id */ public async accept(membershipId: string): Promise { - const memberDetails = await this.getProfileDetails(); + const profileService = new ProfileService(this.wallet, this.api); + const memberDetails = await profileService.get(); const service = new MembershipService(this.wallet, this.api); await service.setVaultContextFromMembershipId(membershipId); const state = { @@ -457,6 +461,48 @@ class MembershipService extends Service { return null; } } + + async rotateMemberKeys(publicKeys: Map): Promise<{ + memberKeys: Map, + keyPair: Keys + }> { + const memberKeys = new Map(); + // generate a new vault key pair + const keyPair = await generateKeyPair(); + + for (let [memberId, publicKey] of publicKeys) { + const memberKeysEncrypter = new Encrypter( + this.wallet, + this.dataEncrypter.keys, + base64ToArray(publicKey) + ); + try { + memberKeys.set(memberId, [await memberKeysEncrypter.encryptMemberKey(keyPair)]); + } catch (error) { + throw new IncorrectEncryptionKey(error); + } + } + return { memberKeys, keyPair }; + } + + async processMemberDetails(memberDetails: { name?: string, avatar?: ArrayBuffer }, cacheOnly?: boolean) { + const processedMemberDetails = {} as ProfileDetails; + if (!this.isPublic) { + if (memberDetails.name) { + processedMemberDetails.name = await this.processWriteString(memberDetails.name); + } + if (memberDetails.avatar) { + const { resourceUrl, resourceTx } = await this.processAvatar(memberDetails.avatar, cacheOnly); + processedMemberDetails.avatarUri = [`arweave:${resourceTx}`, `s3:${resourceUrl}`]; + } + } + return new ProfileDetails(processedMemberDetails); + } + + private async processAvatar(avatar: ArrayBuffer, cacheOnly?: boolean): Promise<{ resourceTx: string, resourceUrl: string }> { + const { processedData, encryptionTags } = await this.processWriteRaw(avatar); + return this.api.uploadFile(processedData, encryptionTags, { cacheOnly, public: false }); + } }; export type MembershipCreateOptions = { diff --git a/src/core/node.ts b/src/core/node.ts index 1ffc0646..44a304be 100644 --- a/src/core/node.ts +++ b/src/core/node.ts @@ -8,6 +8,7 @@ import { Paginated } from '../types/paginated'; import { v4 as uuidv4 } from "uuid"; import { IncorrectEncryptionKey } from '../errors/incorrect-encryption-key'; import { BadRequest } from '../errors/bad-request'; +import { handleListErrors, paginate } from './common'; class NodeService extends Service { objectType: NodeType; @@ -64,7 +65,7 @@ class NodeService extends Service { .map(async (nodeProto: any) => { return await this.processNode(nodeProto, !nodeProto.__public__ && listOptions.shouldDecrypt, nodeProto.__keys__); }); - const { items, errors } = await this.handleListErrors(response.items, promises); + const { items, errors } = await handleListErrors(response.items, promises); return { items, nextToken: response.nextToken, @@ -81,7 +82,7 @@ class NodeService extends Service { const list = async (options: ListOptions & { vaultId: string }) => { return await this.list(options.vaultId, options); } - return await this.paginate(list, { ...options, vaultId }); + return await paginate(list, { ...options, vaultId }); } /** diff --git a/src/core/profile.ts b/src/core/profile.ts index cc52a609..cd48a123 100644 --- a/src/core/profile.ts +++ b/src/core/profile.ts @@ -6,6 +6,9 @@ import { Service } from "./service"; import { objectType } from "../constants"; import { Membership } from "../types/membership"; import { ListOptions } from "../types/query-options"; +import { getEncryptedPayload, handleListErrors, paginate } from "./common"; +import { Encrypter, arrayToString } from "@akord/crypto"; +import { IncorrectEncryptionKey } from "../errors/incorrect-encryption-key"; class ProfileService extends Service { objectType = objectType.PROFILE; @@ -20,7 +23,42 @@ class ProfileService extends Service { shouldCacheDecider: () => CacheBusters.cache }) public async get(): Promise { - return await this.getProfileDetails(); + const user = await this.api.getUser(); + if (user) { + const profileEncrypter = new Encrypter(this.wallet, null, null); + profileEncrypter.decryptedKeys = [ + { + publicKey: this.wallet.publicKeyRaw(), + privateKey: this.wallet.privateKeyRaw() + } + ] + let avatar = null; + const resourceUri = getAvatarUri(new ProfileDetails(user)); + if (resourceUri) { + const { fileData, metadata } = await this.api.downloadFile(resourceUri); + const encryptedPayload = getEncryptedPayload(fileData, metadata); + try { + if (encryptedPayload) { + avatar = await profileEncrypter.decryptRaw(encryptedPayload, false); + } else { + const dataString = arrayToString(new Uint8Array(fileData)); + avatar = await profileEncrypter.decryptRaw(dataString, true); + } + } catch (error) { + throw new IncorrectEncryptionKey(error); + } + } + try { + const decryptedProfile = await profileEncrypter.decryptObject( + user, + ['name'], + ); + return { ...decryptedProfile, avatar } + } catch (error) { + throw new IncorrectEncryptionKey(error); + } + } + return {}; } /** @@ -38,11 +76,12 @@ class ProfileService extends Service { }> { // update profile const user = await this.api.getUser(); - this.setObject(user); - this.setRawDataEncryptionPublicKey(this.wallet.publicKeyRaw()); - this.setIsPublic(false); - const profileDetails = await this.processMemberDetails({ name, avatar }, true); + const service = new MembershipService(this.wallet, this.api); + + service.setRawDataEncryptionPublicKey(this.wallet.publicKeyRaw()); + service.setIsPublic(false); + const profileDetails = await service.processMemberDetails({ name, avatar }, true); const newProfileDetails = new ProfileDetails({ ...user, @@ -61,7 +100,7 @@ class ProfileService extends Service { transactions.push({ id: membership.id, transactionId: transactionId }); return membership; }) - const { errors } = await this.handleListErrors(memberships, membershipPromises); + const { errors } = await handleListErrors(memberships, membershipPromises); return { transactions, errors }; } @@ -69,10 +108,22 @@ class ProfileService extends Service { const list = async (listOptions: ListOptions) => { return await this.api.getMemberships(listOptions); } - return await this.paginate(list, {}); + return await paginate(list, {}); } }; +const getAvatarUri = (profileDetails: ProfileDetails) => { + if (profileDetails.avatarUri && profileDetails.avatarUri.length) { + const avatarUri = [...profileDetails.avatarUri] + .reverse() + .find(resourceUri => resourceUri.startsWith("s3:")) + ?.replace("s3:", ""); + return avatarUri !== "null" && avatarUri; + } else { + return null; + } +} + export { ProfileService } diff --git a/src/core/service.ts b/src/core/service.ts index 34bdddee..c17600e0 100644 --- a/src/core/service.ts +++ b/src/core/service.ts @@ -11,12 +11,9 @@ import { arrayToBase64, base64ToJson, deriveAddress, - EncryptedKeys, - generateKeyPair, - Keys + EncryptedKeys } from "@akord/crypto"; import { objectType, protocolTags, functions, dataTags, encryptionTags, smartweaveTags, AKORD_TAG } from '../constants'; -import lodash from "lodash"; import { Vault } from "../types/vault"; import { Tag, Tags } from "../types/contract"; import { NodeLike } from "../types/node"; @@ -24,8 +21,7 @@ import { Membership } from "../types/membership"; import { Object, ObjectType } from "../types/object"; import { EncryptedPayload } from "@akord/crypto/lib/types"; import { IncorrectEncryptionKey } from "../errors/incorrect-encryption-key"; -import { ProfileDetails } from "../types/profile-details"; -import { ListOptions } from "../types/query-options"; +import { getEncryptedPayload, mergeState } from "./common"; export type EncryptionMetadata = { encryptedKey?: string, @@ -128,21 +124,18 @@ class Service { this.tags = tags; } - async processReadRaw(data: ArrayBuffer | string, metadata: EncryptionMetadata, shouldDecrypt = true): Promise { - if (this.isPublic || !shouldDecrypt) { - return data as ArrayBuffer; - } - - const encryptedPayload = this.getEncryptedPayload(data, metadata); + async processWriteString(data: string): Promise { + if (this.isPublic) return data; + let encryptedPayload: string; try { - if (encryptedPayload) { - return this.dataEncrypter.decryptRaw(encryptedPayload, false); - } else { - return this.dataEncrypter.decryptRaw(data as string); - } + encryptedPayload = await this.dataEncrypter.encryptRaw(stringToArray(data)) as string; } catch (error) { throw new IncorrectEncryptionKey(error); } + const decodedPayload = base64ToJson(encryptedPayload) as any; + decodedPayload.publicAddress = (await this.getActiveKey()).address; + delete decodedPayload.publicKey; + return jsonToBase64(decodedPayload); } async setVaultContext(vaultId: string) { @@ -176,43 +169,48 @@ class Service { } } - protected async getProfileDetails(): Promise { - const user = await this.api.getUser(); - if (user) { - const profileEncrypter = new Encrypter(this.wallet, null, null); - profileEncrypter.decryptedKeys = [ - { - publicKey: this.wallet.publicKeyRaw(), - privateKey: this.wallet.privateKeyRaw() - } - ] - let avatar = null; - const resourceUri = this.getAvatarUri(new ProfileDetails(user)); - if (resourceUri) { - const { fileData, metadata } = await this.api.downloadFile(resourceUri); - const encryptedPayload = this.getEncryptedPayload(fileData, metadata); - try { - if (encryptedPayload) { - avatar = await profileEncrypter.decryptRaw(encryptedPayload, false); - } else { - const dataString = arrayToString(new Uint8Array(fileData)); - avatar = await profileEncrypter.decryptRaw(dataString, true); - } - } catch (error) { - throw new IncorrectEncryptionKey(error); - } - } - try { - const decryptedProfile = await profileEncrypter.decryptObject( - user, - ['name'], - ); - return { ...decryptedProfile, avatar } - } catch (error) { - throw new IncorrectEncryptionKey(error); - } + async uploadState(state: any, cacheOnly = false): Promise { + const signature = await this.signData(state); + const tags = [ + new Tag(dataTags.DATA_TYPE, "State"), + new Tag(smartweaveTags.CONTENT_TYPE, STATE_CONTENT_TYPE), + new Tag(protocolTags.SIGNATURE, signature), + new Tag(protocolTags.SIGNER_ADDRESS, await this.wallet.getAddress()), + new Tag(protocolTags.VAULT_ID, this.vaultId), + new Tag(protocolTags.NODE_TYPE, this.objectType), + ] + if (this.objectType === objectType.MEMBERSHIP) { + tags.push(new Tag(protocolTags.MEMBERSHIP_ID, this.objectId)) + } else if (this.objectType !== objectType.VAULT) { + tags.push(new Tag(protocolTags.NODE_ID, this.objectId)) } - return {}; + const ids = await this.api.uploadData([{ data: state, tags }], { cacheOnly }); + return ids[0]; + } + + async getTxTags(): Promise { + const tags = [ + new Tag(protocolTags.FUNCTION_NAME, this.function), + new Tag(protocolTags.SIGNER_ADDRESS, await this.wallet.getAddress()), + new Tag(protocolTags.VAULT_ID, this.vaultId), + new Tag(protocolTags.TIMESTAMP, JSON.stringify(Date.now())), + new Tag(protocolTags.NODE_TYPE, this.objectType), + new Tag(protocolTags.PUBLIC, this.isPublic ? "true" : "false"), + ] + if (this.groupRef) { + tags.push(new Tag(protocolTags.GROUP_REF, this.groupRef)); + } + if (this.actionRef) { + tags.push(new Tag(protocolTags.ACTION_REF, this.actionRef)); + } + this.tags + ?.filter(tag => tag) + ?.map((tag: string) => + tag?.split(" ").join(",").split(".").join(",").split(",").map((value: string) => + tags.push(new Tag(AKORD_TAG, value.toLowerCase()))) + ); + // remove duplicates + return [...new Map(tags.map(item => [item.value, item])).values()]; } protected async processWriteRaw(data: ArrayBuffer, encryptedKey?: string) { @@ -236,53 +234,21 @@ class Service { return { processedData, encryptionTags: tags } } - protected async getActiveKey() { - return { - address: await deriveAddress(this.dataEncrypter.publicKey), - publicKey: arrayToBase64(this.dataEncrypter.publicKey) - }; - } + protected async processReadRaw(data: ArrayBuffer | string, metadata: EncryptionMetadata, shouldDecrypt = true): Promise { + if (this.isPublic || !shouldDecrypt) { + return data as ArrayBuffer; + } - async processWriteString(data: string): Promise { - if (this.isPublic) return data; - let encryptedPayload: string; + const encryptedPayload = getEncryptedPayload(data, metadata); try { - encryptedPayload = await this.dataEncrypter.encryptRaw(stringToArray(data)) as string; + if (encryptedPayload) { + return this.dataEncrypter.decryptRaw(encryptedPayload, false); + } else { + return this.dataEncrypter.decryptRaw(data as string); + } } catch (error) { throw new IncorrectEncryptionKey(error); } - const decodedPayload = base64ToJson(encryptedPayload) as any; - decodedPayload.publicAddress = (await this.getActiveKey()).address; - delete decodedPayload.publicKey; - return jsonToBase64(decodedPayload); - } - - protected getAvatarUri(profileDetails: ProfileDetails) { - if (profileDetails.avatarUri && profileDetails.avatarUri.length) { - const avatarUri = [...profileDetails.avatarUri].reverse().find(resourceUri => resourceUri.startsWith("s3:"))?.replace("s3:", ""); - return avatarUri !== "null" && avatarUri; - } else { - return null; - } - } - - protected async processAvatar(avatar: ArrayBuffer, cacheOnly?: boolean): Promise<{ resourceTx: string, resourceUrl: string }> { - const { processedData, encryptionTags } = await this.processWriteRaw(avatar); - return this.api.uploadFile(processedData, encryptionTags, { cacheOnly, public: false }); - } - - protected async processMemberDetails(memberDetails: { name?: string, avatar?: ArrayBuffer }, cacheOnly?: boolean) { - const processedMemberDetails = {} as ProfileDetails; - if (!this.isPublic) { - if (memberDetails.name) { - processedMemberDetails.name = await this.processWriteString(memberDetails.name); - } - if (memberDetails.avatar) { - const { resourceUrl, resourceTx } = await this.processAvatar(memberDetails.avatar, cacheOnly); - processedMemberDetails.avatarUri = [`arweave:${resourceTx}`, `s3:${resourceUrl}`]; - } - } - return new ProfileDetails(processedMemberDetails); } protected async processReadString(data: string, shouldDecrypt = true): Promise { @@ -291,18 +257,11 @@ class Service { return arrayToString(decryptedDataRaw); } - protected getEncryptedPayload(data: ArrayBuffer | string, metadata: EncryptionMetadata): EncryptedPayload { - const { encryptedKey, iv } = metadata; - if (encryptedKey && iv) { - return { - encryptedKey, - encryptedData: { - iv: base64ToArray(iv), - ciphertext: data as ArrayBuffer - } - } - } - return null; + protected async getActiveKey() { + return { + address: await deriveAddress(this.dataEncrypter.publicKey), + publicKey: arrayToBase64(this.dataEncrypter.publicKey) + }; } protected async getCurrentState(): Promise { @@ -313,11 +272,11 @@ class Service { protected async mergeAndUploadState(stateUpdates: any): Promise { const currentState = await this.getCurrentState(); - const mergedState = await this.mergeState(currentState, stateUpdates); + const mergedState = mergeState(currentState, stateUpdates); return this.uploadState(mergedState); } - protected async signData(data: any): Promise { + private async signData(data: any): Promise { const privateKeyRaw = this.wallet.signingPrivateKeyRaw(); const signature = await signString( jsonToBase64(data), @@ -325,112 +284,6 @@ class Service { ); return signature; } - - public async uploadState(state: any, cacheOnly = false): Promise { - const signature = await this.signData(state); - const tags = [ - new Tag(dataTags.DATA_TYPE, "State"), - new Tag(smartweaveTags.CONTENT_TYPE, STATE_CONTENT_TYPE), - new Tag(protocolTags.SIGNATURE, signature), - new Tag(protocolTags.SIGNER_ADDRESS, await this.wallet.getAddress()), - new Tag(protocolTags.VAULT_ID, this.vaultId), - new Tag(protocolTags.NODE_TYPE, this.objectType), - ] - if (this.objectType === objectType.MEMBERSHIP) { - tags.push(new Tag(protocolTags.MEMBERSHIP_ID, this.objectId)) - } else if (this.objectType !== objectType.VAULT) { - tags.push(new Tag(protocolTags.NODE_ID, this.objectId)) - } - const ids = await this.api.uploadData([{ data: state, tags }], { cacheOnly }); - return ids[0]; - } - - protected async mergeState(currentState: any, stateUpdates: any) { - let newState = lodash.cloneDeepWith(currentState); - lodash.mergeWith( - newState, - stateUpdates, - function concatArrays(objValue, srcValue) { - if (lodash.isArray(objValue)) { - return objValue.concat(srcValue); - } - }); - return newState; - } - - async getTxTags(): Promise { - const tags = [ - new Tag(protocolTags.FUNCTION_NAME, this.function), - new Tag(protocolTags.SIGNER_ADDRESS, await this.wallet.getAddress()), - new Tag(protocolTags.VAULT_ID, this.vaultId), - new Tag(protocolTags.TIMESTAMP, JSON.stringify(Date.now())), - new Tag(protocolTags.NODE_TYPE, this.objectType), - new Tag(protocolTags.PUBLIC, this.isPublic ? "true" : "false"), - ] - if (this.groupRef) { - tags.push(new Tag(protocolTags.GROUP_REF, this.groupRef)); - } - if (this.actionRef) { - tags.push(new Tag(protocolTags.ACTION_REF, this.actionRef)); - } - this.tags - ?.filter(tag => tag) - ?.map((tag: string) => - tag?.split(" ").join(",").split(".").join(",").split(",").map((value: string) => - tags.push(new Tag(AKORD_TAG, value.toLowerCase()))) - ); - // remove duplicates - return [...new Map(tags.map(item => [item.value, item])).values()]; - } - - protected async handleListErrors(originalItems: Array, promises: Array>) - : Promise<{ items: Array, errors: Array<{ id: string, error: Error }> }> { - const results = await Promise.all(promises.map(p => p.catch(e => e))); - const items = results.filter(result => !(result instanceof Error)); - const errors = results - .map((result, index) => ({ result, index })) - .filter((mapped) => mapped.result instanceof Error) - .map((filtered) => ({ id: (originalItems[filtered.index]).id, error: filtered.result })); - return { items, errors }; - } - - protected async paginate(apiCall: any, listOptions: ListOptions & { vaultId?: string }): Promise> { - let token = undefined; - let results = [] as T[]; - do { - const { items, nextToken } = await apiCall(listOptions); - results = results.concat(items); - token = nextToken; - listOptions.nextToken = nextToken; - if (nextToken === "null") { - token = undefined; - } - } while (token); - return results; - } - - protected async rotateMemberKeys(publicKeys: Map): Promise<{ - memberKeys: Map, - keyPair: Keys - }> { - const memberKeys = new Map(); - // generate a new vault key pair - const keyPair = await generateKeyPair(); - - for (let [memberId, publicKey] of publicKeys) { - const memberKeysEncrypter = new Encrypter( - this.wallet, - this.dataEncrypter.keys, - base64ToArray(publicKey) - ); - try { - memberKeys.set(memberId, [await memberKeysEncrypter.encryptMemberKey(keyPair)]); - } catch (error) { - throw new IncorrectEncryptionKey(error); - } - } - return { memberKeys, keyPair }; - } } export { diff --git a/src/core/vault.ts b/src/core/vault.ts index 7ab417cb..af25ba14 100644 --- a/src/core/vault.ts +++ b/src/core/vault.ts @@ -11,6 +11,8 @@ import { MembershipService } from "./membership"; import lodash from "lodash"; import { NotFound } from "../errors/not-found"; import { BadRequest } from "../errors/bad-request"; +import { handleListErrors, paginate } from "./common"; +import { ProfileService } from "./profile"; class VaultService extends Service { objectType = objectType.VAULT; @@ -61,7 +63,7 @@ class VaultService extends Service { .map(async (vaultProto: Vault) => { return await this.processVault(vaultProto, listOptions.shouldDecrypt, vaultProto.keys); }) as Promise[]; - const { items, errors } = await this.handleListErrors(response.items, promises); + const { items, errors } = await handleListErrors(response.items, promises); return { items, nextToken: response.nextToken, @@ -77,7 +79,7 @@ class VaultService extends Service { const list = async (listOptions: ListOptions) => { return await this.list(listOptions); } - return await this.paginate(list, options); + return await paginate(list, options); } /** @@ -98,7 +100,9 @@ class VaultService extends Service { vaultId = await this.api.initContractId([new Tag(protocolTags.NODE_TYPE, objectType.VAULT)]); } - const memberDetails = await this.getProfileDetails(); + const profileService = new ProfileService(this.wallet, this.api); + const memberDetails = await profileService.get(); + const service = new VaultService(this.wallet, this.api); service.setActionRef(actionRefs.VAULT_CREATE); service.setIsPublic(createOptions.public); @@ -116,12 +120,20 @@ class VaultService extends Service { ].concat(await service.getTxTags()); createOptions.arweaveTags?.map((tag: Tag) => service.arweaveTags.push(tag)); + const memberService = new MembershipService(this.wallet, this.api, service); + memberService.setVaultId(service.vaultId); + memberService.setObjectId(membershipId); + let keys: EncryptedKeys[]; if (!service.isPublic) { - const { memberKeys, keyPair } = await service.rotateMemberKeys(new Map([[membershipId, this.wallet.publicKey()]])); + const { memberKeys, keyPair } = await memberService.rotateMemberKeys( + new Map([[membershipId, this.wallet.publicKey()]]) + ); keys = memberKeys.get(membershipId); service.setRawDataEncryptionPublicKey(keyPair.publicKey); service.setKeys([{ encPublicKey: keys[0].encPublicKey, encPrivateKey: keys[0].encPrivateKey }]); + memberService.setRawDataEncryptionPublicKey(keyPair.publicKey); + memberService.setKeys([{ encPublicKey: keys[0].encPublicKey, encPrivateKey: keys[0].encPrivateKey }]); } const vaultState = { @@ -134,12 +146,10 @@ class VaultService extends Service { const memberState = { keys, - encPublicSigningKey: await service.processWriteString(this.wallet.signingPublicKey()), - memberDetails: await service.processMemberDetails(memberDetails, createOptions.cacheOnly) + encPublicSigningKey: await memberService.processWriteString(this.wallet.signingPublicKey()), + memberDetails: await memberService.processMemberDetails(memberDetails, createOptions.cacheOnly) } - const memberService = new MembershipService(this.wallet, this.api); - memberService.setVaultId(service.vaultId); - memberService.setObjectId(membershipId); + const memberStateTx = await memberService.uploadState(memberState, createOptions.cacheOnly); const data = { vault: vaultStateTx, membership: memberStateTx };