UNPKG

wallet-storage-client

Version:
221 lines 8.76 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProvenTx = void 0; const sdk_1 = require("@bsv/sdk"); const index_client_1 = require("../../../index.client"); const _1 = require("."); class ProvenTx extends _1.EntityBase { /** * Given a txid and optionally its rawTx, create a new ProvenTx object. * * rawTx is fetched if not provided. * * Only succeeds (proven is not undefined) if a proof is confirmed for rawTx, * and hash of rawTx is confirmed to match txid * * The returned ProvenTx and ProvenTxReq objects have not been added to the storage database, * this is optional and can be done by the caller if appropriate. * * @param txid * @param services * @param rawTx * @returns */ static async fromTxid(txid, services, rawTx) { var _a; const r = { proven: undefined, rawTx }; const chain = services.chain; if (!r.rawTx) { const gr = await services.getRawTx(txid); if (!(gr === null || gr === void 0 ? void 0 : gr.rawTx)) // Failing to find anything... return r; r.rawTx = gr.rawTx; } const gmpr = await services.getMerklePath(txid); if (gmpr.merklePath && gmpr.header) { const index = (_a = gmpr.merklePath.path[0].find(l => l.hash === txid)) === null || _a === void 0 ? void 0 : _a.offset; if (index !== undefined) { const api = { created_at: new Date(), updated_at: new Date(), provenTxId: 0, txid, height: gmpr.header.height, index, merklePath: gmpr.merklePath.toBinary(), rawTx: r.rawTx, blockHash: gmpr.header.hash, merkleRoot: gmpr.header.merkleRoot }; r.proven = new ProvenTx(api); } } return r; } constructor(api) { const now = new Date(); super(api || { provenTxId: 0, created_at: now, updated_at: now, txid: '', height: 0, index: 0, merklePath: [], rawTx: [], blockHash: '', merkleRoot: '' }); } updateApi() { /* nothing needed yet... */ } /** * @returns desirialized `MerklePath` object, value is cached. */ getMerklePath() { if (!this._mp) this._mp = sdk_1.MerklePath.fromBinary(this.api.merklePath); return this._mp; } get provenTxId() { return this.api.provenTxId; } set provenTxId(v) { this.api.provenTxId = v; } get created_at() { return this.api.created_at; } set created_at(v) { this.api.created_at = v; } get updated_at() { return this.api.updated_at; } set updated_at(v) { this.api.updated_at = v; } get txid() { return this.api.txid; } set txid(v) { this.api.txid = v; } get height() { return this.api.height; } set height(v) { this.api.height = v; } get index() { return this.api.index; } set index(v) { this.api.index = v; } get merklePath() { return this.api.merklePath; } set merklePath(v) { this.api.merklePath = v; } get rawTx() { return this.api.rawTx; } set rawTx(v) { this.api.rawTx = v; } get blockHash() { return this.api.blockHash; } set blockHash(v) { this.api.blockHash = v; } get merkleRoot() { return this.api.merkleRoot; } set merkleRoot(v) { this.api.merkleRoot = v; } get id() { return this.api.provenTxId; } set id(v) { this.api.provenTxId = v; } get entityName() { return 'ProvenTx'; } get entityTable() { return 'proven_txs'; } equals(ei, syncMap) { const eo = this.toApi(); if (eo.txid != ei.txid || eo.height != ei.height || eo.index != ei.index || !(0, index_client_1.arraysEqual)(eo.merklePath, ei.merklePath) || !(0, index_client_1.arraysEqual)(eo.rawTx, ei.rawTx) || eo.blockHash !== ei.blockHash || eo.merkleRoot !== ei.merkleRoot // equality does not depend on timestamps. // || eo.created_at !== ei.created_at // || eo.updated_at !== ei.updated_at ) return false; if (syncMap) { if (eo.provenTxId !== syncMap.provenTx.idMap[ei.provenTxId]) return false; } else { if (eo.provenTxId !== ei.provenTxId) return false; } return true; } static async mergeFind(storage, userId, ei, syncMap, trx) { const ef = (0, index_client_1.verifyOneOrNone)(await storage.findProvenTxs({ partial: { txid: ei.txid }, trx })); return { found: !!ef, eo: new ProvenTx(ef || { ...ei }), eiId: (0, index_client_1.verifyId)(ei.provenTxId) }; } async mergeNew(storage, userId, syncMap, trx) { this.provenTxId = 0; // TODO: Since these records are a shared resource, the record must be validated before accepting it... this.provenTxId = await storage.insertProvenTx(this.toApi(), trx); } async mergeExisting(storage, since, ei, syncMap, trx) { // ProvenTxs are never updated. return false; } /** * Try to create a new ProvenTx from a ProvenTxReq and GetMerkleProofResultApi * * Otherwise it returns undefined and updates req.status to either 'unknown', 'invalid', or 'unconfirmed' * * @param req * @param gmpResult * @returns */ static async fromReq(req, gmpResult, countsAsAttempt) { if (!req.txid) throw new index_client_1.sdk.WERR_MISSING_PARAMETER('req.txid'); if (!req.rawTx) throw new index_client_1.sdk.WERR_MISSING_PARAMETER('req.rawTx'); if (!req.rawTx) throw new index_client_1.sdk.WERR_INTERNAL('rawTx must be valid'); req.addHistoryNote({ what: 'getMerkleProof result', result: gmpResult, attempts: req.attempts }); if (!gmpResult.name && !gmpResult.merklePath && !gmpResult.error) { // Most likely offline or now services configured. // Does not count as a proof attempt. return undefined; } if (!gmpResult.merklePath) { if (req.created_at) { const reqAgeInMsecs = Date.now() - req.created_at.getTime(); const reqAgeInMinutes = Math.ceil(reqAgeInMsecs < 1 ? 0 : reqAgeInMsecs / (1000 * 60)); if (req.attempts > index_client_1.entity.ProvenTx.getProofAttemptsLimit && reqAgeInMinutes > index_client_1.entity.ProvenTx.getProofMinutes) { // Start the process of setting transactions to 'failed' req.addHistoryNote({ what: 'getMerkleProof invalid', attempts: req.attempts, ageInMinutes: reqAgeInMinutes }); req.notified = false; req.status = 'invalid'; } } return undefined; } if (countsAsAttempt) req.attempts++; const merklePaths = (Array.isArray(gmpResult.merklePath)) ? gmpResult.merklePath : [gmpResult.merklePath]; for (const proof of merklePaths) { try { const now = new Date(); const leaf = proof.path[0].find(leaf => leaf.txid === true && leaf.hash === req.txid); if (!leaf) throw new index_client_1.sdk.WERR_INTERNAL('merkle path does not contain leaf for txid'); const proven = new ProvenTx({ created_at: now, updated_at: now, provenTxId: 0, txid: req.txid, height: proof.blockHeight, index: leaf.offset, merklePath: proof.toBinary(), rawTx: req.rawTx, merkleRoot: gmpResult.header.merkleRoot, blockHash: gmpResult.header.hash }); return proven; } catch (err) { req.addHistoryNote({ what: "getMerkleProof catch", proof, error: index_client_1.sdk.WalletError.fromUnknown(err) }); } } } } exports.ProvenTx = ProvenTx; /** * How high attempts can go before status is forced to invalid */ ProvenTx.getProofAttemptsLimit = 8; /** * How many hours we have to try for a poof */ ProvenTx.getProofMinutes = 60; //# sourceMappingURL=ProvenTx.js.map