feat(offline): add signer service util class
This commit is contained in:
parent
5079b68bdf
commit
bcd57138ca
@ -1 +1,2 @@
|
|||||||
export * from './cache'
|
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