diff --git a/src/store/actionTypes.ts b/src/store/actionTypes.ts index f0e6698..ff33627 100644 --- a/src/store/actionTypes.ts +++ b/src/store/actionTypes.ts @@ -18,3 +18,4 @@ export const SET_RELAY_MAP_UPDATED = 'SET_RELAY_MAP_UPDATED' export const UPDATE_USER_APP_DATA = 'UPDATE_USER_APP_DATA' export const UPDATE_PROCESSED_GIFT_WRAPS = 'UPDATE_PROCESSED_GIFT_WRAPS' +export const SET_D_TAG_FOR_APP_DATA = 'SET_D_TAG_FOR_APP_DATA' diff --git a/src/store/userAppData/action.ts b/src/store/userAppData/action.ts index 23bf9a1..2164aaa 100644 --- a/src/store/userAppData/action.ts +++ b/src/store/userAppData/action.ts @@ -1,6 +1,10 @@ import { UserAppData } from '../../types' import * as ActionTypes from '../actionTypes' -import { UpdateProcessedGiftWraps, UpdateUserAppData } from './types' +import { + SetDTagForAppData, + UpdateProcessedGiftWraps, + UpdateUserAppData +} from './types' export const updateUserAppData = (payload: UserAppData): UpdateUserAppData => ({ type: ActionTypes.UPDATE_USER_APP_DATA, @@ -13,3 +17,8 @@ export const updateProcessedGiftWraps = ( type: ActionTypes.UPDATE_PROCESSED_GIFT_WRAPS, payload }) + +export const setDTagForAppData = (payload: string): SetDTagForAppData => ({ + type: ActionTypes.SET_D_TAG_FOR_APP_DATA, + payload +}) diff --git a/src/store/userAppData/reducer.ts b/src/store/userAppData/reducer.ts index 22c7cb0..dacb375 100644 --- a/src/store/userAppData/reducer.ts +++ b/src/store/userAppData/reducer.ts @@ -24,6 +24,12 @@ const reducer = ( processedGiftWraps: action.payload } + case ActionTypes.SET_D_TAG_FOR_APP_DATA: + return { + ...state, + dTag: action.payload + } + case ActionTypes.RESTORE_STATE: return action.payload.userAppData || null diff --git a/src/store/userAppData/types.ts b/src/store/userAppData/types.ts index 07c207f..c3de135 100644 --- a/src/store/userAppData/types.ts +++ b/src/store/userAppData/types.ts @@ -12,7 +12,13 @@ export interface UpdateProcessedGiftWraps { payload: string[] } +export interface SetDTagForAppData { + type: typeof ActionTypes.SET_D_TAG_FOR_APP_DATA + payload: string +} + export type UserAppDataDispatchTypes = | UpdateUserAppData | UpdateProcessedGiftWraps + | SetDTagForAppData | RestoreState diff --git a/src/types/core.ts b/src/types/core.ts index 8583d4a..cb6c2f4 100644 --- a/src/types/core.ts +++ b/src/types/core.ts @@ -43,6 +43,7 @@ export interface UserAppData { sigits: { [key: string]: Meta } // key will be id of create signature processedGiftWraps: string[] // an array of ids of processed gift wrapped events keyPair?: Keys // this key pair is used for blossom requests authentication + dTag?: string // this will be used for d tag in 30078 kind of events blossomUrls: string[] // array for storing Urls for the files that stores all the sigits and processedGiftWraps on blossom } diff --git a/src/utils/nostr.ts b/src/utils/nostr.ts index 9bade2f..d65561d 100644 --- a/src/utils/nostr.ts +++ b/src/utils/nostr.ts @@ -11,6 +11,7 @@ import { getEventHash, getPublicKey, kinds, + nip04, nip19, nip44, verifyEvent @@ -23,6 +24,7 @@ import { relayController } from '../controllers' import { + setDTagForAppData, updateProcessedGiftWraps, updateUserAppData as updateUserAppDataAction } from '../store/actions' @@ -30,10 +32,54 @@ import { AuthState, Keys } from '../store/auth/types' import { RelaysState } from '../store/relays/types' import store from '../store/store' import { Meta, SignedEvent, UserAppData } from '../types' -import { getHash } from './hash' +import { getDefaultRelayMap } from './relays' import { parseJson, removeLeadingSlash } from './string' import { timeout } from './utils' -import { getDefaultRelayMap } from './relays' + +/** + * Generates a `d` tag for userAppData + */ +const getDTagForUserAppData = async (): Promise => { + const isLoggedIn = store.getState().auth?.loggedIn + const pubkey = store.getState().auth?.usersPubkey + + if (!isLoggedIn || !pubkey) { + throw new Error( + 'For generating d tag user must be logged in and a valid pubkey should exists in app Store' + ) + } + + let dTag = store.getState().userAppData?.dTag + + // if dTag is found in redux store then just return that + if (dTag) return dTag + + // dTag not found is redux store. Generate it. + const unsignedEvent: UnsignedEvent = { + kind: kinds.ShortTextNote, + pubkey: pubkey, + created_at: 0, + tags: [], + content: `938_${pubkey}` + } + + const nostrController = NostrController.getInstance() + const signedEvent = await nostrController + .signEvent(unsignedEvent) + .catch((err) => { + console.error('Failed to sign event for dTag', err) + toast.error(err.message || err) + return null + }) + if (!signedEvent) return null + + dTag = signedEvent.sig + + // save dTag in redux store + store.dispatch(setDTagForAppData(dTag)) + + return dTag +} /** * @param hexKey hex private or public key @@ -377,13 +423,13 @@ export const getUsersAppData = async (): Promise => { } // Generate an identifier for the user's nip78 - const hash = await getHash('938' + usersPubkey) - if (!hash) return null + const dTag = await getDTagForUserAppData() + if (!dTag) return null // Define a filter for fetching events const filter: Filter = { kinds: [kinds.Application], - '#d': [hash] + '#d': [dTag] } const encryptedContent = await relayController @@ -578,14 +624,14 @@ export const updateUsersAppData = async (meta: Meta) => { if (!encryptedContent) return null // generate the identifier for user's appData event - const hash = await getHash('938' + usersPubkey) - if (!hash) return null + const dTag = await getDTagForUserAppData() + if (!dTag) return null const updatedEvent: UnsignedEvent = { kind: kinds.Application, pubkey: usersPubkey!, created_at: unixNow(), - tags: [['d', hash]], + tags: [['d', dTag]], content: encryptedContent } @@ -693,9 +739,10 @@ const uploadUserAppDataToBlossom = async ( // Convert the private key from hex to bytes const secretKey = hexToBytes(privateKey) // Encrypt the JSON string using the secret key - const encrypted = nip44.v2.encrypt( - stringified, - nip44ConversationKey(secretKey, getPublicKey(secretKey)) + const encrypted = await nip04.encrypt( + secretKey, + getPublicKey(secretKey), + stringified ) // Create a blob from the encrypted data @@ -788,10 +835,7 @@ const getUserAppDataFromBlossom = async (url: string, privateKey: string) => { const pubkey = getPublicKey(secret) // Decrypt the encrypted data using the secret and public key - const decrypted = nip44.v2.decrypt( - encrypted, - nip44ConversationKey(secret, pubkey) - ) + const decrypted = await nip04.decrypt(secret, pubkey, encrypted) // Parse the decrypted JSON content const parsedContent = await parseJson<{