mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-10-05 19:28:26 +00:00
Refactor nostrService
This commit is contained in:
+31
-17
@@ -26,7 +26,7 @@ import AppError, { Err } from '../utils/AppError'
|
||||
import { LightningUtils } from '../services/lightning/lightningUtils'
|
||||
import { addSeconds } from 'date-fns/addSeconds'
|
||||
import { Transaction, TransactionStatus, TransactionType } from './Transaction'
|
||||
import { MeltQuoteResponse } from '@cashu/cashu-ts'
|
||||
import { MeltQuoteBolt11Response } from '@cashu/cashu-ts'
|
||||
import { WalletStore } from './WalletStore'
|
||||
import { ProofsStore } from './ProofsStore'
|
||||
import { isSameDay } from 'date-fns/isSameDay'
|
||||
@@ -165,9 +165,13 @@ export const NwcConnectionModel = types.model('NwcConnection', {
|
||||
// eventInFlight.pubkey should = connectionPubkey
|
||||
log.trace('Encrypt response', {connectionPubkey: self.connectionPubkey, requestEventPubkey: requestEvent.pubkey})
|
||||
|
||||
const walletStore = self.getWalletStore()
|
||||
const keys: NostrKeyPair = (yield walletStore.getCachedWalletKeys()).NOSTR
|
||||
|
||||
const encryptedContent = yield NostrClient.encryptNip04(
|
||||
requestEvent.pubkey,
|
||||
JSON.stringify(nwcResponse)
|
||||
requestEvent.pubkey,
|
||||
JSON.stringify(nwcResponse),
|
||||
keys
|
||||
)
|
||||
|
||||
const responseEvent: NostrUnsignedEvent = {
|
||||
@@ -216,7 +220,8 @@ export const NwcConnectionModel = types.model('NwcConnection', {
|
||||
yield NostrClient.publish(
|
||||
responseEvent,
|
||||
self.connectionRelays,
|
||||
false
|
||||
keys,
|
||||
false
|
||||
)
|
||||
}),
|
||||
payInvoice: flow(function* payInvoice(nwcRequest: NwcRequest, encodedInvoice: string, requestEvent: NostrEvent) {
|
||||
@@ -268,7 +273,7 @@ export const NwcConnectionModel = types.model('NwcConnection', {
|
||||
} as NwcError
|
||||
}
|
||||
|
||||
const meltQuote: MeltQuoteResponse = yield walletStore.createLightningMeltQuote(
|
||||
const meltQuote: MeltQuoteBolt11Response = yield walletStore.createLightningMeltQuote(
|
||||
mintBalance.mintUrl,
|
||||
'sat',
|
||||
encodedInvoice,
|
||||
@@ -365,7 +370,7 @@ export const NwcConnectionModel = types.model('NwcConnection', {
|
||||
)
|
||||
|
||||
// TODO barebones implementation, no paging commands support
|
||||
const transactions = lightningTransactions.map(t => {
|
||||
const transactions = lightningTransactions.map((t: Transaction) => {
|
||||
return {
|
||||
type: t.type === TransactionType.TOPUP ? 'incoming' : 'outgoing',
|
||||
invoice: t.paymentRequest,
|
||||
@@ -555,13 +560,16 @@ export const NwcConnectionModel = types.model('NwcConnection', {
|
||||
})
|
||||
}))
|
||||
.actions(self => ({
|
||||
handleNwcRequestTask: flow(function* handleNwcRequestTask(requestEvent: NostrEvent, decryptedNwcRequest?: NwcRequest) {
|
||||
handleNwcRequestTask: flow(function* handleNwcRequestTask(requestEvent: NostrEvent, decryptedNwcRequest?: NwcRequest) {
|
||||
let nwcRequest: NwcRequest
|
||||
if(!decryptedNwcRequest) {
|
||||
|
||||
const walletStore = self.getWalletStore()
|
||||
const keys: NostrKeyPair = (yield walletStore.getCachedWalletKeys()).NOSTR
|
||||
|
||||
const decryptedContent = yield NostrClient.decryptNip04(
|
||||
requestEvent.pubkey,
|
||||
requestEvent.content
|
||||
requestEvent.pubkey,
|
||||
requestEvent.content,
|
||||
keys
|
||||
)
|
||||
|
||||
nwcRequest = JSON.parse(decryptedContent)
|
||||
@@ -708,16 +716,22 @@ export const NwcStoreModel = types
|
||||
const infoEvent: NostrUnsignedEvent = {
|
||||
kind: NWCWalletInfo,
|
||||
pubkey: newConnection.walletPubkey,
|
||||
tags: [],
|
||||
tags: [],
|
||||
content: self.supportedMethods.join(' '),
|
||||
created_at: Math.floor(Date.now() / 1000)
|
||||
created_at: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
|
||||
NostrClient.publish(
|
||||
infoEvent,
|
||||
newConnection.connectionRelays,
|
||||
false
|
||||
)
|
||||
// Fire and forget - publish without waiting
|
||||
;(async () => {
|
||||
const rootStore = getRootStore(self)
|
||||
const keys: NostrKeyPair = (await rootStore.walletStore.getCachedWalletKeys()).NOSTR
|
||||
NostrClient.publish(
|
||||
infoEvent,
|
||||
newConnection.connectionRelays,
|
||||
keys,
|
||||
false
|
||||
)
|
||||
})()
|
||||
}
|
||||
}),
|
||||
removeConnection(connectionToRemove: NwcConnection) {
|
||||
|
||||
@@ -76,12 +76,14 @@ export const WalletProfileStoreModel = types
|
||||
}
|
||||
|
||||
const relaysToPublish: string[] = relaysStore.allUrls
|
||||
const keys: NostrKeyPair = (yield rootStore.walletStore.getCachedWalletKeys() as Promise<any>).NOSTR
|
||||
|
||||
log.debug('[publishToRelays]', 'Publish profile to relays', {profileEvent, relaysToPublish})
|
||||
|
||||
const publishedEvent: Event | undefined = yield NostrClient.publish(
|
||||
profileEvent,
|
||||
relaysToPublish
|
||||
relaysToPublish,
|
||||
keys
|
||||
)
|
||||
|
||||
return publishedEvent
|
||||
|
||||
@@ -117,7 +117,7 @@ const SYNC_STATE_WITH_MINT_TIMEOUT = 10 * 1000
|
||||
|
||||
export const NfcPayScreen = observer(function NfcPayScreen({ route }: Props) {
|
||||
const navigation = useNavigation<any>()
|
||||
const { mintsStore, walletStore, proofsStore, transactionsStore } = useStores()
|
||||
const { mintsStore, walletStore, proofsStore, transactionsStore, walletProfileStore } = useStores()
|
||||
const unitRef = useRef<MintUnit>('sat')
|
||||
const isOnline = useRef<boolean>(true)
|
||||
|
||||
@@ -601,10 +601,13 @@ export const NfcPayScreen = observer(function NfcPayScreen({ route }: Props) {
|
||||
proofs: decodedTokenToSend.proofs,
|
||||
})
|
||||
|
||||
const sentEvent = await NostrClient.encryptAndSendDirectMessageNip17(
|
||||
pubkey,
|
||||
const keys = await walletStore.getCachedWalletKeys()
|
||||
const sentEvent = await NostrClient.encryptAndSendDirectMessageNip17(
|
||||
pubkey,
|
||||
messageContent,
|
||||
relays
|
||||
relays,
|
||||
keys.NOSTR,
|
||||
walletProfileStore.nip05
|
||||
)
|
||||
|
||||
if(!sentEvent) {
|
||||
|
||||
@@ -142,7 +142,8 @@ export const ReceiveScreen = observer(function ReceiveScreen({ route }: Props) {
|
||||
|
||||
if(isLocked) {
|
||||
const lockedToPK = CashuUtils.getP2PKPubkeySecret(decoded.proofs[0].secret)
|
||||
isLockedToWallet = lockedToPK === '02' + (await NostrClient.getNostrKeys()).publicKey
|
||||
const keys = await walletStore.getCachedWalletKeys()
|
||||
isLockedToWallet = lockedToPK === '02' + keys.NOSTR.publicKey
|
||||
}
|
||||
|
||||
log.trace('decoded token', {decoded, isLocked, isLockedToWallet})
|
||||
|
||||
@@ -932,10 +932,13 @@ export const SendScreen = observer(function SendScreen({ route }: Props) {
|
||||
})
|
||||
}
|
||||
|
||||
const sentEvent = await NostrClient.encryptAndSendDirectMessageNip17(
|
||||
contactToSendTo.pubkey,
|
||||
const keys = await walletStore.getCachedWalletKeys()
|
||||
const sentEvent = await NostrClient.encryptAndSendDirectMessageNip17(
|
||||
contactToSendTo.pubkey,
|
||||
messageContent!,
|
||||
relaysToShareTo
|
||||
relaysToShareTo,
|
||||
keys.NOSTR,
|
||||
walletProfileStore.nip05
|
||||
)
|
||||
|
||||
setIsNostrDMSending(false)
|
||||
|
||||
@@ -501,10 +501,13 @@ export const TopupScreen = observer(function TopupScreen({ route }: Props) {
|
||||
content = content + `Memo: ${memo}`
|
||||
}
|
||||
|
||||
const sentEvent = await NostrClient.encryptAndSendDirectMessageNip17(
|
||||
receiverPubkey as string,
|
||||
const keys = await walletStore.getCachedWalletKeys()
|
||||
const sentEvent = await NostrClient.encryptAndSendDirectMessageNip17(
|
||||
receiverPubkey as string,
|
||||
content as string,
|
||||
relaysToShareTo
|
||||
relaysToShareTo,
|
||||
keys.NOSTR,
|
||||
walletProfileStore.nip05
|
||||
)
|
||||
|
||||
setIsNostrDMSending(false)
|
||||
|
||||
@@ -370,7 +370,10 @@ export const WalletScreen = observer(function WalletScreen({ route }: Props) {
|
||||
// Real foreground – run your normal logic (e.g., unlock check, etc.)
|
||||
log.trace('[handleAppStateChange] WalletScreen active again')
|
||||
performChecks()
|
||||
NostrClient.reconnectToRelays().catch(e => false)
|
||||
NostrClient.reconnectToRelays({
|
||||
hasDeviceId: !!walletProfileStore.device,
|
||||
onReconnect: () => nwcStore.listenForNwcEvents()
|
||||
}).catch(e => false)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import {NostrKeyPair} from './keyChain'
|
||||
import {log} from './logService'
|
||||
import AppError, { Err } from '../utils/AppError'
|
||||
import { MinibitsClient } from './minibitsService'
|
||||
import { rootStoreInstance } from '../models'
|
||||
import { WalletTask } from './walletService'
|
||||
import { Relay } from 'nostr-tools/relay'
|
||||
|
||||
@@ -60,8 +59,6 @@ const _searchRelays: string[] = ['wss://relay.nostr.band', 'wss://relay.noswhere
|
||||
|
||||
let _pool: any = undefined
|
||||
|
||||
const {walletProfileStore, nwcStore, walletStore} = rootStoreInstance
|
||||
|
||||
const getRelayPool = function () {
|
||||
if(!_pool) {
|
||||
_pool = new SimplePool()
|
||||
@@ -88,12 +85,12 @@ const getAllRelays = function () {
|
||||
return [..._minibitsRelays, ..._defaultPublicRelays].map(url => getNormalizedRelayUrl(url))
|
||||
}
|
||||
|
||||
const getNostrKeys = async function () {
|
||||
const keys = await walletStore.getCachedWalletKeys()
|
||||
return keys.NOSTR
|
||||
export type ReconnectToRelaysOptions = {
|
||||
hasDeviceId: boolean
|
||||
onReconnect?: () => void
|
||||
}
|
||||
|
||||
const reconnectToRelays = async function () {
|
||||
const reconnectToRelays = async function (options: ReconnectToRelaysOptions) {
|
||||
const pool = getRelayPool()
|
||||
const connections = pool.listConnectionStatus()
|
||||
|
||||
@@ -101,22 +98,22 @@ const reconnectToRelays = async function () {
|
||||
|
||||
let isRefreshSubNeeded: boolean = false
|
||||
|
||||
for (const conn of Array.from(connections)) {
|
||||
for (const conn of Array.from(connections)) {
|
||||
if(conn[1] === false) {
|
||||
pool.ensureRelay(conn[0])
|
||||
isRefreshSubNeeded = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recreate subscriptions if all relays down
|
||||
if(isRefreshSubNeeded) {
|
||||
log.trace('[reconnectToRelays] Refreshing Nostr subscriptions')
|
||||
WalletTask.receiveEventsFromRelaysQueue().catch(e => false)
|
||||
|
||||
if(!walletProfileStore.device) {
|
||||
nwcStore.listenForNwcEvents()
|
||||
if(!options.hasDeviceId && options.onReconnect) {
|
||||
options.onReconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -191,13 +188,13 @@ const encodeNprofile = function (pubkey: string, relays: string[] = []): string
|
||||
}*/
|
||||
|
||||
|
||||
const encryptNip04 = async function (
|
||||
receiverPubkey: string,
|
||||
content: string
|
||||
const encryptNip04 = async function (
|
||||
receiverPubkey: string,
|
||||
content: string,
|
||||
keys: NostrKeyPair
|
||||
): Promise<string> {
|
||||
try {
|
||||
const keys: NostrKeyPair = await getNostrKeys()
|
||||
const encryptedContent = await encrypt(keys.privateKey, receiverPubkey, content)
|
||||
const encryptedContent = await encrypt(keys.privateKey, receiverPubkey, content)
|
||||
|
||||
return encryptedContent
|
||||
|
||||
@@ -208,11 +205,10 @@ const encryptNip04 = async function (
|
||||
|
||||
|
||||
const decryptNip04 = async function(
|
||||
senderPubKey: string,
|
||||
encryptedContent: string
|
||||
senderPubKey: string,
|
||||
encryptedContent: string,
|
||||
keys: NostrKeyPair
|
||||
): Promise<string> {
|
||||
|
||||
const keys: NostrKeyPair = await getNostrKeys()
|
||||
const decryptedContent = await decrypt(keys.privateKey, senderPubKey, encryptedContent)
|
||||
|
||||
log.trace('[decryptNip04]', {decryptedContent})
|
||||
@@ -224,14 +220,14 @@ const decryptNip04 = async function(
|
||||
const encryptAndSendDirectMessageNip17 = async function (
|
||||
recipientPublicKey: string,
|
||||
message: string,
|
||||
relays: string[]
|
||||
relays: string[],
|
||||
keys: NostrKeyPair,
|
||||
senderNip05: string
|
||||
) {
|
||||
|
||||
const keys: NostrKeyPair = await getNostrKeys()
|
||||
const directMessageEvent: NostrEventTemplate = {
|
||||
created_at: Math.ceil(Date.now() / 1000),
|
||||
kind: PrivateDirectMessage,
|
||||
tags: [['p', recipientPublicKey], ['from', walletProfileStore.nip05]],
|
||||
tags: [['p', recipientPublicKey], ['from', senderNip05]],
|
||||
content: message,
|
||||
}
|
||||
|
||||
@@ -264,11 +260,10 @@ const encryptAndSendDirectMessageNip17 = async function (
|
||||
|
||||
|
||||
const decryptDirectMessageNip17 = async function (
|
||||
wrappedEvent: NostrEvent
|
||||
) {
|
||||
const keys: NostrKeyPair = await getNostrKeys()
|
||||
|
||||
// log.trace('[decryptDirectMessageNip59]', {keys})
|
||||
wrappedEvent: NostrEvent,
|
||||
keys: NostrKeyPair
|
||||
) {
|
||||
// log.trace('[decryptDirectMessageNip59]', {keys})
|
||||
|
||||
const decryptedEvent = unwrapEvent(
|
||||
wrappedEvent,
|
||||
@@ -284,12 +279,10 @@ const decryptDirectMessageNip17 = async function (
|
||||
const publish = async function (
|
||||
event: NostrUnsignedEvent,
|
||||
relays: string[],
|
||||
retrievePublishedEvent: boolean = true
|
||||
keys: NostrKeyPair,
|
||||
retrievePublishedEvent: boolean = true
|
||||
): Promise<NostrEvent | void> {
|
||||
try{
|
||||
|
||||
const keys: NostrKeyPair = await getNostrKeys()
|
||||
|
||||
const privateKeyBytes = hexToBytes(keys.privateKey)
|
||||
const finalEvent = finalizeEvent(event, privateKeyBytes)
|
||||
|
||||
@@ -620,12 +613,11 @@ const findZapRequest = function (message: string): string | undefined {
|
||||
|
||||
|
||||
export const NostrClient = { // TODO split helper functions to separate module
|
||||
getRelayPool,
|
||||
getRelayPool,
|
||||
getDefaultRelays,
|
||||
getMinibitsRelays,
|
||||
getSearchRelays,
|
||||
getAllRelays,
|
||||
getNostrKeys,
|
||||
reconnectToRelays,
|
||||
getNpubkey,
|
||||
getHexkey,
|
||||
|
||||
@@ -262,20 +262,22 @@ const _nwcRequestHandler = async function(remoteData: NotifyNwcRequestData) {
|
||||
}
|
||||
|
||||
const _getRemoteData = async function(remoteMessage: FirebaseMessagingTypes.RemoteMessage) {
|
||||
|
||||
|
||||
const {encrypted} = remoteMessage.data!
|
||||
|
||||
if (!encrypted) {
|
||||
throw new AppError(Err.VALIDATION_ERROR, 'Unknown remote message data received', {data: remoteMessage.data})
|
||||
throw new AppError(Err.VALIDATION_ERROR, 'Unknown remote message data received', {data: remoteMessage.data})
|
||||
}
|
||||
|
||||
const {walletStore} = rootStoreInstance
|
||||
const keys = (await walletStore.getCachedWalletKeys()).NOSTR
|
||||
const serverPubkey = MINIBIT_SERVER_NOSTR_PUBKEY
|
||||
const decrypted = await NostrClient.decryptNip04(serverPubkey, encrypted as string)
|
||||
const decrypted = await NostrClient.decryptNip04(serverPubkey, encrypted as string, keys)
|
||||
|
||||
if (!decrypted) {
|
||||
throw new AppError(Err.VALIDATION_ERROR, 'Unknown remote message data received', {data: remoteMessage.data})
|
||||
}
|
||||
|
||||
|
||||
return JSON.parse(decrypted)
|
||||
}
|
||||
|
||||
|
||||
@@ -2254,7 +2254,8 @@ const handleClaimTask = async function (params: {
|
||||
}
|
||||
|
||||
const encryptedToken = claimedToken.token
|
||||
const encodedToken = await NostrClient.decryptNip04(MINIBIT_SERVER_NOSTR_PUBKEY, encryptedToken)
|
||||
const keys = (await walletStore.getCachedWalletKeys()).NOSTR
|
||||
const encodedToken = await NostrClient.decryptNip04(MINIBIT_SERVER_NOSTR_PUBKEY, encryptedToken, keys)
|
||||
|
||||
log.debug('[handleClaimTask] decrypted token', {encodedToken})
|
||||
|
||||
@@ -2427,18 +2428,19 @@ const receiveEventsFromRelaysQueue = async function (): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
const handleReceivedEventTask = async function (encryptedEvent: NostrEvent): Promise<WalletTaskResult> {
|
||||
const handleReceivedEventTask = async function (encryptedEvent: NostrEvent): Promise<WalletTaskResult> {
|
||||
try {
|
||||
let directMessageEvent: NostrEvent | UnsignedEvent | undefined = undefined
|
||||
let decryptedMessage: string | undefined = undefined
|
||||
|
||||
const keys = (await walletStore.getCachedWalletKeys()).NOSTR
|
||||
|
||||
if (encryptedEvent.kind === EncryptedDirectMessage) {
|
||||
directMessageEvent = encryptedEvent
|
||||
decryptedMessage = await NostrClient.decryptNip04(encryptedEvent.pubkey, encryptedEvent.content)
|
||||
decryptedMessage = await NostrClient.decryptNip04(encryptedEvent.pubkey, encryptedEvent.content, keys)
|
||||
}
|
||||
|
||||
if (encryptedEvent.kind === GiftWrap) {
|
||||
directMessageEvent = await NostrClient.decryptDirectMessageNip17(encryptedEvent)
|
||||
directMessageEvent = await NostrClient.decryptDirectMessageNip17(encryptedEvent, keys)
|
||||
decryptedMessage = directMessageEvent.content
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user