wallet-storage-client
Version:
Client only Wallet Storage
221 lines • 8.76 kB
JavaScript
;
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