feat: implemented relay controller and use that for fetching and publishing events #149
@ -1,11 +1,8 @@
|
||||
import NDK, { NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk'
|
||||
import {
|
||||
Event,
|
||||
EventTemplate,
|
||||
Filter,
|
||||
VerifiedEvent,
|
||||
kinds,
|
||||
nip19,
|
||||
validateEvent,
|
||||
verifyEvent
|
||||
} from 'nostr-tools'
|
||||
@ -13,7 +10,7 @@ import { toast } from 'react-toastify'
|
||||
import { EventEmitter } from 'tseep'
|
||||
import { NostrController, relayController } from '.'
|
||||
import { localCache } from '../services'
|
||||
import { NostrJoiningBlock, ProfileMetadata, RelaySet } from '../types'
|
||||
import { ProfileMetadata, RelaySet } from '../types'
|
||||
import {
|
||||
findRelayListAndUpdateCache,
|
||||
findRelayListInCache,
|
||||
@ -21,7 +18,6 @@ import {
|
||||
getMostPopularRelays,
|
||||
getUserRelaySet,
|
||||
isOlderThanOneWeek,
|
||||
queryNip05,
|
||||
unixNow
|
||||
} from '../utils'
|
||||
|
||||
@ -218,128 +214,6 @@ export class MetadataController extends EventEmitter {
|
||||
})
|
||||
}
|
||||
|
||||
public getNostrJoiningBlockNumber = async (
|
||||
hexKey: string
|
||||
): Promise<NostrJoiningBlock | null> => {
|
||||
const relaySet = await this.findRelayListMetadata(hexKey)
|
||||
|
||||
const userRelays: string[] = []
|
||||
|
||||
// find user's relays
|
||||
if (relaySet.write.length > 0) {
|
||||
userRelays.push(...relaySet.write)
|
||||
} else {
|
||||
const metadata = await this.findMetadata(hexKey)
|
||||
if (!metadata) return null
|
||||
|
||||
const metadataContent = this.extractProfileMetadataContent(metadata)
|
||||
|
||||
if (metadataContent?.nip05) {
|
||||
const nip05Profile = await queryNip05(metadataContent.nip05)
|
||||
|
||||
if (nip05Profile && nip05Profile.pubkey === hexKey) {
|
||||
userRelays.push(...nip05Profile.relays)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (userRelays.length === 0) return null
|
||||
|
||||
// filter for finding user's first kind 0 event
|
||||
const eventFilter: Filter = {
|
||||
kinds: [kinds.Metadata],
|
||||
authors: [hexKey]
|
||||
}
|
||||
|
||||
// find user's kind 0 event published on user's relays
|
||||
const event = await relayController.fetchEvent(eventFilter, userRelays)
|
||||
|
||||
if (event) {
|
||||
const { created_at } = event
|
||||
|
||||
// initialize job request
|
||||
const jobEventTemplate: EventTemplate = {
|
||||
content: '',
|
||||
created_at: unixNow(),
|
||||
kind: 68001,
|
||||
tags: [
|
||||
['i', `${created_at * 1000}`],
|
||||
['j', 'blockChain-block-number']
|
||||
]
|
||||
}
|
||||
|
||||
// sign job request event
|
||||
const jobSignedEvent =
|
||||
await this.nostrController.signEvent(jobEventTemplate)
|
||||
|
||||
const relays = [
|
||||
'wss://relay.damus.io',
|
||||
'wss://relay.primal.net',
|
||||
'wss://relayable.org'
|
||||
]
|
||||
|
||||
await relayController.publish(jobSignedEvent, relays).catch((err) => {
|
||||
console.error(
|
||||
'Error occurred in publish blockChain-block-number DVM job',
|
||||
err
|
||||
)
|
||||
})
|
||||
|
||||
const subscribeWithTimeout = (
|
||||
subscription: NDKSubscription,
|
||||
timeoutMs: number
|
||||
): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const eventHandler = (event: NDKEvent) => {
|
||||
subscription.stop()
|
||||
resolve(event.content)
|
||||
}
|
||||
|
||||
subscription.on('event', eventHandler)
|
||||
|
||||
// Set up a timeout to stop the subscription after a specified time
|
||||
const timeout = setTimeout(() => {
|
||||
subscription.stop() // Stop the subscription
|
||||
reject(new Error('Subscription timed out')) // Reject the promise with a timeout error
|
||||
}, timeoutMs)
|
||||
|
||||
// Handle subscription close event
|
||||
subscription.on('close', () => clearTimeout(timeout))
|
||||
})
|
||||
}
|
||||
|
||||
const dvmNDK = new NDK({
|
||||
explicitRelayUrls: relays
|
||||
})
|
||||
|
||||
await dvmNDK.connect(2000)
|
||||
|
||||
// filter for getting DVM job's result
|
||||
const sub = dvmNDK.subscribe({
|
||||
kinds: [68002 as number],
|
||||
'#e': [jobSignedEvent.id],
|
||||
'#p': [jobSignedEvent.pubkey]
|
||||
})
|
||||
|
||||
// asynchronously get block number from dvm job with 20 seconds timeout
|
||||
const dvmJobResult = await subscribeWithTimeout(sub, 20000)
|
||||
|
||||
const encodedEventPointer = nip19.neventEncode({
|
||||
id: event.id,
|
||||
relays: userRelays,
|
||||
author: event.pubkey,
|
||||
kind: event.kind
|
||||
})
|
||||
|
||||
return {
|
||||
block: parseInt(dvmJobResult),
|
||||
encodedEventPointer
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
public validate = (event: Event) => validateEvent(event) && verifyEvent(event)
|
||||
|
||||
public getEmptyMetadataEvent = (): Event => {
|
||||
|
@ -12,7 +12,12 @@ import { MetadataController } from '../../controllers'
|
||||
import { getProfileSettingsRoute } from '../../routes'
|
||||
import { State } from '../../store/rootReducer'
|
||||
import { NostrJoiningBlock, ProfileMetadata } from '../../types'
|
||||
import { getRoboHashPicture, hexToNpub, shorten } from '../../utils'
|
||||
import {
|
||||
getNostrJoiningBlockNumber,
|
||||
getRoboHashPicture,
|
||||
hexToNpub,
|
||||
shorten
|
||||
} from '../../utils'
|
||||
import styles from './style.module.scss'
|
||||
import { Container } from '../../components/Container'
|
||||
|
||||
@ -51,8 +56,7 @@ export const ProfilePage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (pubkey) {
|
||||
metadataController
|
||||
.getNostrJoiningBlockNumber(pubkey)
|
||||
getNostrJoiningBlockNumber(pubkey)
|
||||
.then((res) => {
|
||||
setNostrJoiningBlock(res)
|
||||
})
|
||||
|
@ -26,7 +26,11 @@ import { setMetadataEvent } from '../../../store/actions'
|
||||
import { LoadingSpinner } from '../../../components/LoadingSpinner'
|
||||
import { LoginMethods } from '../../../store/auth/types'
|
||||
import { SmartToy } from '@mui/icons-material'
|
||||
import { getRoboHashPicture, unixNow } from '../../../utils'
|
||||
import {
|
||||
getNostrJoiningBlockNumber,
|
||||
getRoboHashPicture,
|
||||
unixNow
|
||||
} from '../../../utils'
|
||||
import { Container } from '../../../components/Container'
|
||||
|
||||
export const ProfileSettingsPage = () => {
|
||||
@ -71,8 +75,7 @@ export const ProfileSettingsPage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (pubkey) {
|
||||
metadataController
|
||||
.getNostrJoiningBlockNumber(pubkey)
|
||||
getNostrJoiningBlockNumber(pubkey)
|
||||
.then((res) => {
|
||||
setNostrJoiningBlock(res)
|
||||
})
|
||||
|
135
src/utils/dvm.ts
Normal file
135
src/utils/dvm.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import { EventTemplate, Filter, kinds, nip19 } from 'nostr-tools'
|
||||
import { queryNip05, unixNow } from '.'
|
||||
import {
|
||||
MetadataController,
|
||||
NostrController,
|
||||
relayController
|
||||
} from '../controllers'
|
||||
import { NostrJoiningBlock } from '../types'
|
||||
import NDK, { NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk'
|
||||
|
||||
export const getNostrJoiningBlockNumber = async (
|
||||
hexKey: string
|
||||
): Promise<NostrJoiningBlock | null> => {
|
||||
const metadataController = new MetadataController()
|
||||
|
||||
const relaySet = await metadataController.findRelayListMetadata(hexKey)
|
||||
|
||||
const userRelays: string[] = []
|
||||
|
||||
// find user's relays
|
||||
if (relaySet.write.length > 0) {
|
||||
userRelays.push(...relaySet.write)
|
||||
} else {
|
||||
const metadata = await metadataController.findMetadata(hexKey)
|
||||
if (!metadata) return null
|
||||
|
||||
const metadataContent =
|
||||
metadataController.extractProfileMetadataContent(metadata)
|
||||
|
||||
if (metadataContent?.nip05) {
|
||||
const nip05Profile = await queryNip05(metadataContent.nip05)
|
||||
|
||||
if (nip05Profile && nip05Profile.pubkey === hexKey) {
|
||||
userRelays.push(...nip05Profile.relays)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (userRelays.length === 0) return null
|
||||
|
||||
// filter for finding user's first kind 0 event
|
||||
const eventFilter: Filter = {
|
||||
kinds: [kinds.Metadata],
|
||||
authors: [hexKey]
|
||||
}
|
||||
|
||||
// find user's kind 0 event published on user's relays
|
||||
const event = await relayController.fetchEvent(eventFilter, userRelays)
|
||||
|
||||
if (event) {
|
||||
const { created_at } = event
|
||||
|
||||
// initialize job request
|
||||
const jobEventTemplate: EventTemplate = {
|
||||
content: '',
|
||||
created_at: unixNow(),
|
||||
kind: 68001,
|
||||
tags: [
|
||||
['i', `${created_at * 1000}`],
|
||||
['j', 'blockChain-block-number']
|
||||
]
|
||||
}
|
||||
|
||||
const nostrController = NostrController.getInstance()
|
||||
|
||||
// sign job request event
|
||||
const jobSignedEvent = await nostrController.signEvent(jobEventTemplate)
|
||||
|
||||
const relays = [
|
||||
'wss://relay.damus.io',
|
||||
'wss://relay.primal.net',
|
||||
'wss://relayable.org'
|
||||
]
|
||||
|
||||
await relayController.publish(jobSignedEvent, relays).catch((err) => {
|
||||
console.error(
|
||||
'Error occurred in publish blockChain-block-number DVM job',
|
||||
err
|
||||
)
|
||||
})
|
||||
|
||||
const subscribeWithTimeout = (
|
||||
subscription: NDKSubscription,
|
||||
timeoutMs: number
|
||||
): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const eventHandler = (event: NDKEvent) => {
|
||||
subscription.stop()
|
||||
resolve(event.content)
|
||||
}
|
||||
|
||||
subscription.on('event', eventHandler)
|
||||
|
||||
// Set up a timeout to stop the subscription after a specified time
|
||||
const timeout = setTimeout(() => {
|
||||
subscription.stop() // Stop the subscription
|
||||
reject(new Error('Subscription timed out')) // Reject the promise with a timeout error
|
||||
}, timeoutMs)
|
||||
|
||||
// Handle subscription close event
|
||||
subscription.on('close', () => clearTimeout(timeout))
|
||||
})
|
||||
}
|
||||
|
||||
const dvmNDK = new NDK({
|
||||
explicitRelayUrls: relays
|
||||
})
|
||||
|
||||
await dvmNDK.connect(2000)
|
||||
|
||||
// filter for getting DVM job's result
|
||||
const sub = dvmNDK.subscribe({
|
||||
kinds: [68002 as number],
|
||||
'#e': [jobSignedEvent.id],
|
||||
'#p': [jobSignedEvent.pubkey]
|
||||
})
|
||||
|
||||
// asynchronously get block number from dvm job with 20 seconds timeout
|
||||
const dvmJobResult = await subscribeWithTimeout(sub, 20000)
|
||||
|
||||
const encodedEventPointer = nip19.neventEncode({
|
||||
id: event.id,
|
||||
relays: userRelays,
|
||||
author: event.pubkey,
|
||||
kind: event.kind
|
||||
})
|
||||
|
||||
return {
|
||||
block: parseInt(dvmJobResult),
|
||||
encodedEventPointer
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
export * from './crypto'
|
||||
export * from './dvm'
|
||||
export * from './hash'
|
||||
export * from './localStorage'
|
||||
export * from './mark'
|
||||
|
Loading…
Reference in New Issue
Block a user