UNPKG

@bsv/wallet-toolbox

Version:

BRC100 conforming wallet, wallet storage and wallet signer components

294 lines (283 loc) 8.57 kB
import { StorageReader } from '../StorageReader' import { TableProvenTx } from '../schema/tables/TableProvenTx' import { TableProvenTxReq } from '../schema/tables/TableProvenTxReq' import { TableCertificate } from '../schema/tables/TableCertificate' import { TableCertificateField } from '../schema/tables/TableCertificateField' import { TableOutputBasket } from '../schema/tables/TableOutputBasket' import { TableTransaction } from '../schema/tables/TableTransaction' import { TableOutput } from '../schema/tables/TableOutput' import { TableOutputTag } from '../schema/tables/TableOutputTag' import { TableOutputTagMap } from '../schema/tables/TableOutputTagMap' import { TableTxLabel } from '../schema/tables/TableTxLabel' import { TableTxLabelMap } from '../schema/tables/TableTxLabelMap' import { TableCommission } from '../schema/tables/TableCommission' import { FindForUserSincePagedArgs, RequestSyncChunkArgs, SyncChunk } from '../../sdk/WalletStorage.interfaces' import { verifyTruthy } from '../../utility/utilityHelpers' import { WERR_INVALID_OPERATION, WERR_INVALID_PARAMETER } from '../../sdk/WERR_errors' /** * Gets the next sync chunk of updated data from un-remoted storage (could be using a remote DB connection). * @param storage * @param args * @returns */ export async function getSyncChunk(storage: StorageReader, args: RequestSyncChunkArgs): Promise<SyncChunk> { const r: SyncChunk = { fromStorageIdentityKey: args.fromStorageIdentityKey, toStorageIdentityKey: args.toStorageIdentityKey, userIdentityKey: args.identityKey } let itemCount = args.maxItems let roughSize = args.maxRoughSize let i = 0 let done = false const user = verifyTruthy(await storage.findUserByIdentityKey(args.identityKey)) if (!args.since || user.updated_at > new Date(args.since)) r.user = user const chunkers: ChunkerArgs[] = [ { name: 'provenTx', maxDivider: 100, preAdd: () => { r.provenTxs = [] }, addItem: (i: TableProvenTx) => { r.provenTxs!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.getProvenTxsForUser(args) } }, { name: 'outputBasket', maxDivider: 1, preAdd: () => { r.outputBaskets = [] }, addItem: (i: TableOutputBasket) => { r.outputBaskets!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.findOutputBaskets({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) } }, { name: 'outputTag', maxDivider: 1, preAdd: () => { r.outputTags = [] }, addItem: (i: TableOutputTag) => { r.outputTags!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.findOutputTags({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) } }, { name: 'txLabel', maxDivider: 1, preAdd: () => { r.txLabels = [] }, addItem: (i: TableTxLabel) => { r.txLabels!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.findTxLabels({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) } }, { name: 'transaction', maxDivider: 25, preAdd: () => { r.transactions = [] }, addItem: (i: TableTransaction) => { r.transactions!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.findTransactions({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) } }, { name: 'output', maxDivider: 25, preAdd: () => { r.outputs = [] }, addItem: (i: TableOutput) => { r.outputs!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.findOutputs({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) } }, { name: 'txLabelMap', maxDivider: 1, preAdd: () => { r.txLabelMaps = [] }, addItem: (i: TableTxLabelMap) => { r.txLabelMaps!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.getTxLabelMapsForUser(args) } }, { name: 'outputTagMap', maxDivider: 1, preAdd: () => { r.outputTagMaps = [] }, addItem: (i: TableOutputTagMap) => { r.outputTagMaps!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.getOutputTagMapsForUser(args) } }, { name: 'certificate', maxDivider: 25, preAdd: () => { r.certificates = [] }, addItem: (i: TableCertificate) => { r.certificates!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.findCertificates({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) } }, { name: 'certificateField', maxDivider: 25, preAdd: () => { r.certificateFields = [] }, addItem: (i: TableCertificateField) => { r.certificateFields!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.findCertificateFields({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) } }, { name: 'commission', maxDivider: 25, preAdd: () => { r.commissions = [] }, addItem: (i: TableCommission) => { r.commissions!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.findCommissions({ partial: { userId: args.userId }, since: args.since, paged: args.paged }) } }, { name: 'provenTxReq', maxDivider: 100, preAdd: () => { r.provenTxReqs = [] }, addItem: (i: TableProvenTxReq) => { r.provenTxReqs!.push(i) }, findItems: async (storage: StorageReader, args: FindForUserSincePagedArgs) => { return await storage.getProvenTxReqsForUser(args) } } ] const addItems = async (a: ChunkerArgs) => { if (i >= args.offsets.length) { done = true return } let { offset, name: oname } = args.offsets[i++] if (a.name !== oname) throw new WERR_INVALID_PARAMETER('offsets', `in dependency order. '${a.name}' expected, found ${oname}.`) let preAddCalled = false for (; !done; ) { const limit = Math.min(itemCount, Math.max(10, args.maxItems / a.maxDivider)) if (limit <= 0) break const items = await a.findItems(storage, { userId: user.userId, since: args.since, paged: { limit, offset } }) checkEntityValues(items) if (!preAddCalled) { a.preAdd() preAddCalled = true } if (items.length === 0) break for (const item of items) { offset++ a.addItem(item) itemCount-- roughSize -= JSON.stringify(item).length if (itemCount <= 0 || roughSize < 0) { done = true break } } } } for (; !done; ) { for (const c of chunkers) { await addItems(c) } } return r } type ChunkerArgs = { name: string maxDivider: number preAdd: () => void addItem: (i: any) => void findItems: (storage: StorageReader, args: FindForUserSincePagedArgs) => Promise<any[]> } function checkIsDate(v: any) { if (!(v instanceof Date)) throw new WERR_INVALID_OPERATION('bad date') } function checkEntityValues(es: object[]) { for (const e of es) { checkIsDate(e['created_at']) checkIsDate(e['updated_at']) for (const key of Object.keys(e)) if (e[key] === null) throw new WERR_INVALID_OPERATION() } }