UNPKG

@bsv/wallet-toolbox-client

Version:
334 lines 13.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EntitySyncState = void 0; const WERR_errors_1 = require("../../../sdk/WERR_errors"); const utilityHelpers_1 = require("../../../utility/utilityHelpers"); const EntityBase_1 = require("./EntityBase"); const EntityCertificate_1 = require("./EntityCertificate"); const EntityCertificateField_1 = require("./EntityCertificateField"); const EntityCommission_1 = require("./EntityCommission"); const EntityOutput_1 = require("./EntityOutput"); const EntityOutputBasket_1 = require("./EntityOutputBasket"); const EntityOutputTag_1 = require("./EntityOutputTag"); const EntityOutputTagMap_1 = require("./EntityOutputTagMap"); const EntityProvenTx_1 = require("./EntityProvenTx"); const EntityProvenTxReq_1 = require("./EntityProvenTxReq"); const EntityTransaction_1 = require("./EntityTransaction"); const EntityTxLabel_1 = require("./EntityTxLabel"); const EntityTxLabelMap_1 = require("./EntityTxLabelMap"); const EntityUser_1 = require("./EntityUser"); const MergeEntity_1 = require("./MergeEntity"); class EntitySyncState extends EntityBase_1.EntityBase { constructor(api) { const now = new Date(); super(api || { syncStateId: 0, created_at: now, updated_at: now, userId: 0, storageIdentityKey: '', storageName: '', init: false, refNum: '', status: 'unknown', when: undefined, errorLocal: undefined, errorOther: undefined, satoshis: undefined, syncMap: JSON.stringify((0, EntityBase_1.createSyncMap)()) }); this.errorLocal = this.api.errorLocal ? JSON.parse(this.api.errorLocal) : undefined; this.errorOther = this.api.errorOther ? JSON.parse(this.api.errorOther) : undefined; this.syncMap = JSON.parse(this.api.syncMap); this.validateSyncMap(this.syncMap); } validateSyncMap(sm) { for (const key of Object.keys(sm)) { const esm = sm[key]; if (typeof esm.maxUpdated_at === 'string') esm.maxUpdated_at = new Date(esm.maxUpdated_at); } } static async fromStorage(storage, userIdentityKey, remoteSettings) { const { user } = (0, utilityHelpers_1.verifyTruthy)(await storage.findOrInsertUser(userIdentityKey)); let { syncState: api } = (0, utilityHelpers_1.verifyTruthy)(await storage.findOrInsertSyncStateAuth({ userId: user.userId, identityKey: userIdentityKey }, remoteSettings.storageIdentityKey, remoteSettings.storageName)); if (!api.syncMap || api.syncMap === '{}') api.syncMap = JSON.stringify((0, EntityBase_1.createSyncMap)()); const ss = new EntitySyncState(api); return ss; } /** * Handles both insert and update based on id value: zero indicates insert. * @param storage * @param notSyncMap if not new and true, excludes updating syncMap in storage. * @param trx */ async updateStorage(storage, notSyncMap, trx) { this.updated_at = new Date(); this.updateApi(notSyncMap && this.id > 0); if (this.id === 0) { await storage.insertSyncState(this.api); } else { const update = { ...this.api }; if (notSyncMap) delete update.syncMap; delete update.created_at; await storage.updateSyncState((0, utilityHelpers_1.verifyId)(this.id), update, trx); } } updateApi(notSyncMap) { this.api.errorLocal = this.apiErrorLocal; this.api.errorOther = this.apiErrorOther; if (!notSyncMap) this.api.syncMap = this.apiSyncMap; } // Pass through api properties set created_at(v) { this.api.created_at = v; } get created_at() { return this.api.created_at; } set updated_at(v) { this.api.updated_at = v; } get updated_at() { return this.api.updated_at; } set userId(v) { this.api.userId = v; } get userId() { return this.api.userId; } set storageIdentityKey(v) { this.api.storageIdentityKey = v; } get storageIdentityKey() { return this.api.storageIdentityKey; } set storageName(v) { this.api.storageName = v; } get storageName() { return this.api.storageName; } set init(v) { this.api.init = v; } get init() { return this.api.init; } set refNum(v) { this.api.refNum = v; } get refNum() { return this.api.refNum; } set status(v) { this.api.status = v; } get status() { return this.api.status; } set when(v) { this.api.when = v; } get when() { return this.api.when; } set satoshis(v) { this.api.satoshis = v; } get satoshis() { return this.api.satoshis; } get apiErrorLocal() { return this.errorToString(this.errorLocal); } get apiErrorOther() { return this.errorToString(this.errorOther); } get apiSyncMap() { return JSON.stringify(this.syncMap); } get id() { return this.api.syncStateId; } set id(id) { this.api.syncStateId = id; } get entityName() { return 'syncState'; } get entityTable() { return 'sync_states'; } static mergeIdMap(fromMap, toMap) { for (const [key, value] of Object.entries(fromMap)) { const fromValue = fromMap[key]; const toValue = toMap[key]; if (toValue !== undefined && toValue !== fromValue) throw new WERR_errors_1.WERR_INVALID_PARAMETER('syncMap', `an unmapped id or the same mapped id. ${key} maps to ${toValue} not equal to ${fromValue}`); if (toValue === undefined) toMap[key] = value; } } /** * Merge additions to the syncMap * @param iSyncMap */ mergeSyncMap(iSyncMap) { EntitySyncState.mergeIdMap(iSyncMap.provenTx.idMap, this.syncMap.provenTx.idMap); EntitySyncState.mergeIdMap(iSyncMap.outputBasket.idMap, this.syncMap.outputBasket.idMap); EntitySyncState.mergeIdMap(iSyncMap.transaction.idMap, this.syncMap.transaction.idMap); EntitySyncState.mergeIdMap(iSyncMap.provenTxReq.idMap, this.syncMap.provenTxReq.idMap); EntitySyncState.mergeIdMap(iSyncMap.txLabel.idMap, this.syncMap.txLabel.idMap); EntitySyncState.mergeIdMap(iSyncMap.output.idMap, this.syncMap.output.idMap); EntitySyncState.mergeIdMap(iSyncMap.outputTag.idMap, this.syncMap.outputTag.idMap); EntitySyncState.mergeIdMap(iSyncMap.certificate.idMap, this.syncMap.certificate.idMap); EntitySyncState.mergeIdMap(iSyncMap.commission.idMap, this.syncMap.commission.idMap); } /** * Eliminate any properties besides code and description */ errorToString(e) { if (!e) return undefined; const es = { code: e.code, description: e.description, stack: e.stack }; return JSON.stringify(es); } equals(ei, syncMap) { return false; } async mergeNew(storage, userId, syncMap, trx) { } async mergeExisting(storage, since, ei, syncMap, trx) { return false; } makeRequestSyncChunkArgs(forIdentityKey, forStorageIdentityKey, maxRoughSize, maxItems) { const a = { identityKey: forIdentityKey, maxRoughSize: maxRoughSize || 10000000, maxItems: maxItems || 1000, offsets: [], since: this.when, fromStorageIdentityKey: this.storageIdentityKey, toStorageIdentityKey: forStorageIdentityKey }; for (const ess of [ this.syncMap.provenTx, this.syncMap.outputBasket, this.syncMap.outputTag, this.syncMap.txLabel, this.syncMap.transaction, this.syncMap.output, this.syncMap.txLabelMap, this.syncMap.outputTagMap, this.syncMap.certificate, this.syncMap.certificateField, this.syncMap.commission, this.syncMap.provenTxReq ]) { if (!ess || !ess.entityName) debugger; a.offsets.push({ name: ess.entityName, offset: ess.count }); } return a; } static syncChunkSummary(c) { let log = ''; log += `SYNC CHUNK SUMMARY from storage: ${c.fromStorageIdentityKey} to storage: ${c.toStorageIdentityKey} for user: ${c.userIdentityKey} `; if (c.user) log += ` USER activeStorage ${c.user.activeStorage}\n`; if (!!c.provenTxs) { log += ` PROVEN_TXS\n`; for (const r of c.provenTxs) { log += ` ${r.provenTxId} ${r.txid}\n`; } } if (!!c.provenTxReqs) { log += ` PROVEN_TX_REQS\n`; for (const r of c.provenTxReqs) { log += ` ${r.provenTxReqId} ${r.txid} ${r.status} ${r.provenTxId || ''}\n`; } } if (!!c.transactions) { log += ` TRANSACTIONS\n`; for (const r of c.transactions) { log += ` ${r.transactionId} ${r.txid} ${r.status} ${r.provenTxId || ''} sats:${r.satoshis}\n`; } } if (!!c.outputs) { log += ` OUTPUTS\n`; for (const r of c.outputs) { log += ` ${r.outputId} ${r.txid}.${r.vout} ${r.transactionId} ${r.spendable ? 'spendable' : ''} sats:${r.satoshis}\n`; } } return log; } async processSyncChunk(writer, args, chunk) { var _a; const mes = [ new MergeEntity_1.MergeEntity(chunk.provenTxs, EntityProvenTx_1.EntityProvenTx.mergeFind, this.syncMap.provenTx), new MergeEntity_1.MergeEntity(chunk.outputBaskets, EntityOutputBasket_1.EntityOutputBasket.mergeFind, this.syncMap.outputBasket), new MergeEntity_1.MergeEntity(chunk.outputTags, EntityOutputTag_1.EntityOutputTag.mergeFind, this.syncMap.outputTag), new MergeEntity_1.MergeEntity(chunk.txLabels, EntityTxLabel_1.EntityTxLabel.mergeFind, this.syncMap.txLabel), new MergeEntity_1.MergeEntity(chunk.transactions, EntityTransaction_1.EntityTransaction.mergeFind, this.syncMap.transaction), new MergeEntity_1.MergeEntity(chunk.outputs, EntityOutput_1.EntityOutput.mergeFind, this.syncMap.output), new MergeEntity_1.MergeEntity(chunk.txLabelMaps, EntityTxLabelMap_1.EntityTxLabelMap.mergeFind, this.syncMap.txLabelMap), new MergeEntity_1.MergeEntity(chunk.outputTagMaps, EntityOutputTagMap_1.EntityOutputTagMap.mergeFind, this.syncMap.outputTagMap), new MergeEntity_1.MergeEntity(chunk.certificates, EntityCertificate_1.EntityCertificate.mergeFind, this.syncMap.certificate), new MergeEntity_1.MergeEntity(chunk.certificateFields, EntityCertificateField_1.EntityCertificateField.mergeFind, this.syncMap.certificateField), new MergeEntity_1.MergeEntity(chunk.commissions, EntityCommission_1.EntityCommission.mergeFind, this.syncMap.commission), new MergeEntity_1.MergeEntity(chunk.provenTxReqs, EntityProvenTxReq_1.EntityProvenTxReq.mergeFind, this.syncMap.provenTxReq) ]; let updates = 0; let inserts = 0; let maxUpdated_at = undefined; let done = true; // Merge User if (chunk.user) { const ei = chunk.user; const { found, eo } = await EntityUser_1.EntityUser.mergeFind(writer, this.userId, ei); if (found) { if (await eo.mergeExisting(writer, args.since, ei)) { maxUpdated_at = (0, utilityHelpers_1.maxDate)(maxUpdated_at, ei.updated_at); updates++; } } } // Merge everything else... for (const me of mes) { const r = await me.merge(args.since, writer, this.userId, this.syncMap); // The counts become the offsets for the next chunk. me.esm.count += ((_a = me.stateArray) === null || _a === void 0 ? void 0 : _a.length) || 0; updates += r.updates; inserts += r.inserts; maxUpdated_at = (0, utilityHelpers_1.maxDate)(maxUpdated_at, me.esm.maxUpdated_at); // If any entity type either did not report results or if there were at least one, then we aren't done. if (me.stateArray === undefined || me.stateArray.length > 0) done = false; //if (me.stateArray !== undefined && me.stateArray.length > 0) // console.log(`merged ${me.stateArray?.length} ${me.esm.entityName} ${r.inserts} inserted, ${r.updates} updated`); } if (done) { // Next batch starts further in the future with offsets of zero. this.when = maxUpdated_at; for (const me of mes) me.esm.count = 0; } await this.updateStorage(writer, false); return { done, maxUpdated_at, updates, inserts }; } } exports.EntitySyncState = EntitySyncState; //# sourceMappingURL=EntitySyncState.js.map