feat(offline): add signer service util class
This commit is contained in:
parent
5079b68bdf
commit
bcd57138ca
@ -1 +1,2 @@
|
||||
export * from './cache'
|
||||
export * from './signer'
|
||||
|
143
src/services/signer/index.ts
Normal file
143
src/services/signer/index.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user