feat(offline): add signer service util class

This commit is contained in:
enes 2025-01-17 21:02:18 +01:00
parent 5079b68bdf
commit bcd57138ca
2 changed files with 144 additions and 0 deletions

View File

@ -1 +1,2 @@
export * from './cache' export * from './cache'
export * from './signer'

View File

@ -0,0 +1,143 @@
import { toast } from 'react-toastify'
import { Meta, SignedEventContent } from '../../types'
import {
parseCreateSignatureEventContent,
parseNostrEvent,
SigitStatus,
SignStatus
} from '../../utils'
import { MetaParseError } from '../../types/errors/MetaParseError'
import { verifyEvent } from 'nostr-tools'
import { appPrivateRoutes, appPublicRoutes } from '../../routes'
export class SignerService {
#signers: `npub1${string}`[] = []
#nextSigner: `npub1${string}` | undefined
#signatures = new Map<`npub1${string}`, string>()
#signersStatus = new Map<`npub1${string}`, SignStatus>()
#lastSignerSig: string | undefined
constructor(source: Meta) {
this.#process(source.createSignature, source.docSignatures)
}
getNextSigner = () => {
return this.#nextSigner
}
isNextSigner = (npub: `npub1${string}`) => {
return this.#nextSigner === npub
}
isLastSigner = (npub: `npub1${string}`) => {
const lastIndex = this.#signers.length - 1
const npubIndex = this.#signers.indexOf(npub)
return npubIndex === lastIndex
}
#isFullySigned = () => {
const signedBy = Object.keys(this.#signatures) as `npub1${string}`[]
const isCompletelySigned = this.#signers.every((signer) =>
signedBy.includes(signer)
)
return isCompletelySigned
}
getSignedStatus = () => {
return this.#isFullySigned() ? SigitStatus.Complete : SigitStatus.Partial
}
getSignerStatus = (npub: `npub1${string}`) => {
return this.#signersStatus.get(npub)
}
getNavigate = (npub: `npub1${string}`) => {
return this.isNextSigner(npub)
? appPrivateRoutes.sign
: appPublicRoutes.verify
}
getLastSignerSig = () => {
return this.#lastSignerSig
}
#process = (
createSignature: string,
docSignatures: { [key: `npub1${string}`]: string }
) => {
try {
const createSignatureEvent = parseNostrEvent(createSignature)
const { signers } = parseCreateSignatureEventContent(
createSignatureEvent.content
)
const getPrevSignerSig = (npub: `npub1${string}`) => {
if (signers[0] === npub) {
return createSignatureEvent.sig
}
// Find the index of signer
const currentSignerIndex = signers.findIndex(
(signer) => signer === npub
)
// Return if could not found user in signer's list
if (currentSignerIndex === -1) return
// Find prev signer
const prevSigner = signers[currentSignerIndex - 1]
// Get the signature of prev signer
return this.#signatures.get(prevSigner)
}
this.#signers = [...signers]
for (const npub in docSignatures) {
try {
// Parse each signature event
const event = parseNostrEvent(docSignatures[npub as `npub1${string}`])
this.#signatures.set(npub as `npub1${string}`, event.sig)
const isValidSignature = verifyEvent(event)
if (isValidSignature) {
const prevSignersSig = getPrevSignerSig(npub as `npub1${string}`)
const signedEvent: SignedEventContent = JSON.parse(event.content)
if (
signedEvent.prevSig &&
prevSignersSig &&
signedEvent.prevSig === prevSignersSig
) {
this.#signersStatus.set(
npub as `npub1${string}`,
SignStatus.Signed
)
this.#lastSignerSig = event.sig
}
} else {
this.#signersStatus.set(
npub as `npub1${string}`,
SignStatus.Invalid
)
}
} catch (error) {
this.#signersStatus.set(npub as `npub1${string}`, SignStatus.Invalid)
}
}
this.#signers
.filter((s) => !this.#signatures.has(s))
.forEach((s) => this.#signersStatus.set(s, SignStatus.Pending))
// Get the first signer that hasn't signed
const nextSigner = this.#signers.find((s) => !this.#signatures.has(s))
if (nextSigner) {
this.#nextSigner = nextSigner
this.#signersStatus.set(nextSigner, SignStatus.Awaiting)
}
} catch (error) {
if (error instanceof MetaParseError) {
toast.error(error.message)
console.error(error.name, error.message, error.cause, error.context)
} else {
console.error('Unexpected error', error)
}
}
}
}