Releasing new design #161
@ -1,11 +1,8 @@
|
|||||||
import NDK, { NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk'
|
|
||||||
import {
|
import {
|
||||||
Event,
|
Event,
|
||||||
EventTemplate,
|
|
||||||
Filter,
|
Filter,
|
||||||
VerifiedEvent,
|
VerifiedEvent,
|
||||||
kinds,
|
kinds,
|
||||||
nip19,
|
|
||||||
validateEvent,
|
validateEvent,
|
||||||
verifyEvent
|
verifyEvent
|
||||||
} from 'nostr-tools'
|
} from 'nostr-tools'
|
||||||
@ -13,7 +10,7 @@ import { toast } from 'react-toastify'
|
|||||||
import { EventEmitter } from 'tseep'
|
import { EventEmitter } from 'tseep'
|
||||||
import { NostrController, relayController } from '.'
|
import { NostrController, relayController } from '.'
|
||||||
import { localCache } from '../services'
|
import { localCache } from '../services'
|
||||||
import { NostrJoiningBlock, ProfileMetadata, RelaySet } from '../types'
|
import { ProfileMetadata, RelaySet } from '../types'
|
||||||
import {
|
import {
|
||||||
findRelayListAndUpdateCache,
|
findRelayListAndUpdateCache,
|
||||||
findRelayListInCache,
|
findRelayListInCache,
|
||||||
@ -21,7 +18,6 @@ import {
|
|||||||
getMostPopularRelays,
|
getMostPopularRelays,
|
||||||
getUserRelaySet,
|
getUserRelaySet,
|
||||||
isOlderThanOneWeek,
|
isOlderThanOneWeek,
|
||||||
queryNip05,
|
|
||||||
unixNow
|
unixNow
|
||||||
} from '../utils'
|
} 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 validate = (event: Event) => validateEvent(event) && verifyEvent(event)
|
||||||
|
|
||||||
public getEmptyMetadataEvent = (): Event => {
|
public getEmptyMetadataEvent = (): Event => {
|
||||||
|
@ -12,7 +12,12 @@ import { MetadataController } from '../../controllers'
|
|||||||
import { getProfileSettingsRoute } from '../../routes'
|
import { getProfileSettingsRoute } from '../../routes'
|
||||||
import { State } from '../../store/rootReducer'
|
import { State } from '../../store/rootReducer'
|
||||||
import { NostrJoiningBlock, ProfileMetadata } from '../../types'
|
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 styles from './style.module.scss'
|
||||||
import { Container } from '../../components/Container'
|
import { Container } from '../../components/Container'
|
||||||
|
|
||||||
@ -51,8 +56,7 @@ export const ProfilePage = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pubkey) {
|
if (pubkey) {
|
||||||
metadataController
|
getNostrJoiningBlockNumber(pubkey)
|
||||||
.getNostrJoiningBlockNumber(pubkey)
|
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setNostrJoiningBlock(res)
|
setNostrJoiningBlock(res)
|
||||||
})
|
})
|
||||||
|
@ -26,7 +26,11 @@ import { setMetadataEvent } from '../../../store/actions'
|
|||||||
import { LoadingSpinner } from '../../../components/LoadingSpinner'
|
import { LoadingSpinner } from '../../../components/LoadingSpinner'
|
||||||
import { LoginMethods } from '../../../store/auth/types'
|
import { LoginMethods } from '../../../store/auth/types'
|
||||||
import { SmartToy } from '@mui/icons-material'
|
import { SmartToy } from '@mui/icons-material'
|
||||||
import { getRoboHashPicture, unixNow } from '../../../utils'
|
import {
|
||||||
|
getNostrJoiningBlockNumber,
|
||||||
|
getRoboHashPicture,
|
||||||
|
unixNow
|
||||||
|
} from '../../../utils'
|
||||||
import { Container } from '../../../components/Container'
|
import { Container } from '../../../components/Container'
|
||||||
|
|
||||||
export const ProfileSettingsPage = () => {
|
export const ProfileSettingsPage = () => {
|
||||||
@ -71,8 +75,7 @@ export const ProfileSettingsPage = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pubkey) {
|
if (pubkey) {
|
||||||
metadataController
|
getNostrJoiningBlockNumber(pubkey)
|
||||||
.getNostrJoiningBlockNumber(pubkey)
|
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setNostrJoiningBlock(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 './crypto'
|
||||||
|
export * from './dvm'
|
||||||
export * from './hash'
|
export * from './hash'
|
||||||
export * from './localStorage'
|
export * from './localStorage'
|
||||||
export * from './mark'
|
export * from './mark'
|
||||||
|
Loading…
Reference in New Issue
Block a user