wallet-storage
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
593 lines • 25.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StorageMySQLDojoReader = void 0;
const sdk_1 = require("@bsv/sdk");
const index_all_1 = require("../../index.all");
const index_all_2 = require("../index.all");
const sdk_2 = require("../../sdk");
const StorageReader_1 = require("../StorageReader");
class StorageMySQLDojoReader extends StorageReader_1.StorageReader {
constructor(options) {
super(options);
if (!options.knex)
throw new index_all_1.sdk.WERR_INVALID_PARAMETER('options.knex', `valid`);
this.knex = options.knex;
}
async destroy() {
var _a;
await ((_a = this.knex) === null || _a === void 0 ? void 0 : _a.destroy());
}
async transaction(scope, trx) {
if (trx)
return await scope(trx);
return await this.knex.transaction(async (knextrx) => {
const trx = knextrx;
return await scope(trx);
});
}
toDb(trx) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const db = !trx ? this.knex : trx;
this.whenLastAccess = new Date();
return db;
}
async readSettings(trx) {
const d = (0, index_all_1.verifyOne)(await this.toDb(trx)('settings'));
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
storageIdentityKey: (0, index_all_1.verifyHexString)(d.dojoIdentityKey),
storageName: d.dojoName || `${this.chain} Legacy Import`,
chain: this.chain,
dbtype: "MySQL",
maxOutputScript: 256
};
if (r.storageName.startsWith('staging') && this.chain !== 'test')
throw new index_all_1.sdk.WERR_INVALID_PARAMETER('chain', `in aggreement with storage chain ${r.storageName}`);
this._settings = r;
return r;
}
setupQuery(table, args) {
let q = this.toDb(args.trx)(table);
if (args.partial && Object.keys(args.partial).length > 0)
q.where(args.partial);
if (args.since)
q.where('updated_at', '>=', this.validateDateForWhere(args.since));
if (args.paged) {
q.limit(args.paged.limit);
q.offset(args.paged.offset || 0);
}
return q;
}
findOutputBasketsQuery(args) {
return this.setupQuery('output_baskets', args);
}
async findOutputBaskets(args) {
const q = this.findOutputBasketsQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
basketId: (0, index_all_1.verifyInteger)(d.basketId),
userId: (0, index_all_1.verifyInteger)(d.userId),
name: (0, index_all_1.verifyTruthy)(d.name).trim().toLowerCase(),
numberOfDesiredUTXOs: (0, index_all_1.verifyInteger)(d.numberOfDesiredUTXOs),
minimumDesiredUTXOValue: (0, index_all_1.verifyInteger)(d.minimumDesiredUTXOValue),
isDeleted: !!d.isDeleted
};
rs.push(r);
}
return this.validateEntities(rs, undefined, ['isDeleted']);
}
findTxLabelsQuery(args) {
return this.setupQuery('tx_labels', args);
}
async findTxLabels(args) {
const q = this.findTxLabelsQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
txLabelId: (0, index_all_1.verifyInteger)(d.txLabelId),
userId: (0, index_all_1.verifyInteger)(d.userId),
label: (0, index_all_1.verifyTruthy)(d.label).trim().toLowerCase(),
isDeleted: !!d.isDeleted
};
rs.push(r);
}
return this.validateEntities(rs, undefined, ['isDeleted']);
}
findOutputTagsQuery(args) {
return this.setupQuery('output_tags', args);
}
async findOutputTags(args) {
const q = this.findOutputTagsQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
outputTagId: (0, index_all_1.verifyInteger)(d.outputTagId),
userId: (0, index_all_1.verifyInteger)(d.userId),
tag: (0, index_all_1.verifyTruthy)(d.tag).trim().toLowerCase(),
isDeleted: !!d.isDeleted
};
rs.push(r);
}
return this.validateEntities(rs, undefined, ['isDeleted']);
}
findTransactionsQuery(args, count) {
if (args.partial.rawTx)
throw new index_all_1.sdk.WERR_INVALID_PARAMETER('args.partial.rawTx', `undefined. Transactions may not be found by rawTx value.`);
if (args.partial.inputBEEF)
throw new index_all_1.sdk.WERR_INVALID_PARAMETER('args.partial.inputBEEF', `undefined. Transactions may not be found by inputBEEF value.`);
const q = this.setupQuery('transactions', args);
if (args.status && args.status.length > 0)
q.whereIn('status', args.status);
if (args.noRawTx && !count) {
const columns = index_all_2.table.transactionColumnsWithoutRawTx.map(c => `transactions.${c}`);
q.select(columns);
}
return q;
}
async findTransactions(args) {
const q = this.findTransactionsQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
transactionId: (0, index_all_1.verifyInteger)(d.transactionId),
userId: (0, index_all_1.verifyInteger)(d.userId),
status: (0, index_all_1.verifyTruthy)(convertTxStatus(d.status)),
reference: forceToBase64(d.referenceNumber),
isOutgoing: !!d.isOutgoing,
satoshis: (0, index_all_1.verifyInteger)(d.amount),
description: (0, index_all_1.verifyTruthy)(d.note || ''),
provenTxId: verifyOptionalInteger(d.provenTxId),
version: verifyOptionalInteger(d.version),
lockTime: verifyOptionalInteger(d.lockTime),
txid: nullToUndefined(d.txid),
inputBEEF: d.beef ? Array.from(d.beef) : undefined,
rawTx: d.rawTransaction ? Array.from(d.rawTransaction) : undefined,
};
rs.push(r);
}
return this.validateEntities(rs, undefined, ['isOutgoing']);
}
findCommissionsQuery(args) {
if (args.partial.lockingScript)
throw new index_all_1.sdk.WERR_INVALID_PARAMETER('args.partial.lockingScript', `undefined. Commissions may not be found by lockingScript value.`);
return this.setupQuery('commissions', args);
}
async findCommissions(args) {
const q = this.findCommissionsQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
commissionId: (0, index_all_1.verifyInteger)(d.commissionId),
userId: (0, index_all_1.verifyInteger)(d.userId),
transactionId: (0, index_all_1.verifyInteger)(d.transactionId),
satoshis: (0, index_all_1.verifyInteger)(d.satoshis),
keyOffset: (0, index_all_1.verifyTruthy)(d.keyOffset).trim(),
isRedeemed: !!d.isRedeemed,
lockingScript: Array.from((0, index_all_1.verifyTruthy)(d.outputScript))
};
rs.push(r);
}
return this.validateEntities(rs, undefined, ['isRedeemed']);
}
limitString(s, maxLen) {
if (s.length > maxLen)
s = s.slice(0, maxLen);
return s;
}
findOutputsQuery(args, count) {
if (args.partial.lockingScript)
throw new index_all_1.sdk.WERR_INVALID_PARAMETER('args.partial.lockingScript', `undefined. Outputs may not be found by lockingScript value.`);
const q = this.setupQuery('outputs', args);
if (args.noScript && !count) {
const columns = index_all_2.table.outputColumnsWithoutLockingScript.map(c => `outputs.${c}`);
q.select(columns);
}
return q;
}
async findOutputs(args) {
const q = this.findOutputsQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
outputId: (0, index_all_1.verifyInteger)(d.outputId),
userId: (0, index_all_1.verifyInteger)(d.userId),
transactionId: (0, index_all_1.verifyInteger)(d.transactionId),
basketId: verifyOptionalInteger(d.basketId),
spendable: !!d.spendable,
change: d.providedBy !== 'you' && d.purpose === 'change',
outputDescription: (d.description || '').trim(),
vout: (0, index_all_1.verifyInteger)(d.vout),
satoshis: (0, index_all_1.verifyInteger)(d.amount),
providedBy: (0, index_all_1.verifyTruthy)(d.providedBy || '').trim().toLowerCase().replace('dojo', 'storage'),
purpose: (0, index_all_1.verifyTruthy)(d.purpose || '').trim().toLowerCase(),
type: (0, index_all_1.verifyTruthy)(d.type).trim(),
txid: nullToUndefined(d.txid),
senderIdentityKey: (0, index_all_1.verifyOptionalHexString)(d.senderIdentityKey),
derivationPrefix: nullToUndefined(d.derivationPrefix),
derivationSuffix: nullToUndefined(d.derivationSuffix),
customInstructions: nullToUndefined(d.customInstruction),
spentBy: verifyOptionalInteger(d.spentBy),
sequenceNumber: undefined,
spendingDescription: nullToUndefined(d.spendingDescription),
scriptLength: verifyOptionalInteger(d.scriptLength),
scriptOffset: verifyOptionalInteger(d.scriptOffset),
lockingScript: d.outputScript ? Array.from(d.outputScript) : undefined,
};
rs.push(r);
}
return this.validateEntities(rs, undefined, ['spendable', 'change']);
}
findCertificatesQuery(args) {
const q = this.setupQuery('certificates', args);
if (args.certifiers && args.certifiers.length > 0)
q.whereIn('certifier', args.certifiers);
if (args.types && args.types.length > 0)
q.whereIn('type', args.types);
return q;
}
async findCertificates(args) {
const q = this.findCertificatesQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
certificateId: (0, index_all_1.verifyInteger)(d.certificateId),
userId: (0, index_all_1.verifyInteger)(d.userId),
type: (0, index_all_1.verifyTruthy)(d.type).trim(), // base64
serialNumber: (0, index_all_1.verifyTruthy)(d.serialNumber).trim(), // base64
certifier: (0, index_all_1.verifyHexString)(d.certifier),
subject: (0, index_all_1.verifyHexString)(d.subject),
revocationOutpoint: (0, index_all_1.verifyTruthy)(d.revocationOutpoint).trim().toLowerCase(),
signature: (0, index_all_1.verifyHexString)(d.signature),
verifier: (0, index_all_1.verifyOptionalHexString)(d.validationKey),
isDeleted: !!d.isDeleted
};
rs.push(r);
}
return this.validateEntities(rs, undefined, ['isDeleted']);
}
findCertificateFieldsQuery(args) {
return this.setupQuery('certificate_fields', args);
}
async findCertificateFields(args) {
const q = this.findCertificateFieldsQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
userId: (0, index_all_1.verifyInteger)(d.userId),
certificateId: (0, index_all_1.verifyInteger)(d.certificateId),
fieldName: (0, index_all_1.verifyTruthy)(d.fieldName).trim().toLowerCase(),
fieldValue: (0, index_all_1.verifyTruthy)(d.fieldValue).trim(), // base64
masterKey: (0, index_all_1.verifyTruthy)(d.masterKey).trim(), // base64
};
rs.push(r);
}
return this.validateEntities(rs);
}
async findSyncStates(args) {
const q = this.setupQuery('sync_state', args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
syncStateId: (0, index_all_1.verifyInteger)(d.syncStateId),
userId: (0, index_all_1.verifyInteger)(d.userId),
storageIdentityKey: (0, index_all_1.verifyHexString)(d.storageIdentityKey),
storageName: (0, index_all_1.verifyTruthy)(d.storageName || 'legacy importer').trim().toLowerCase(),
status: convertSyncStatus(d.status),
init: !!d.init,
refNum: (0, index_all_1.verifyTruthy)(d.refNum),
syncMap: (0, index_all_1.verifyTruthy)(d.syncMap),
when: d.when ? this.validateDate(d.when) : undefined,
satoshis: verifyOptionalInteger(d.total),
errorLocal: nullToUndefined(d.errorLocal),
errorOther: nullToUndefined(d.errorOther),
};
rs.push(r);
}
return this.validateEntities(rs, undefined, ['init']);
}
async findUsers(args) {
const q = this.setupQuery('users', args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
userId: (0, index_all_1.verifyId)(d.userId),
identityKey: (0, index_all_1.verifyTruthy)(d.identityKey)
};
rs.push(r);
}
return this.validateEntities(rs);
}
getProvenTxsForUserQuery(args) {
const k = this.toDb(args.trx);
let q = k('proven_txs').where(function () {
this.whereExists(k.select('*').from('transactions').whereRaw(`proven_txs.provenTxId = transactions.provenTxId and transactions.userId = ${args.userId}`));
});
if (args.paged) {
q = q.limit(args.paged.limit);
q = q.offset(args.paged.offset || 0);
}
if (args.since)
q = q.where('updated_at', '>=', args.since);
return q;
}
async getProvenTxsForUser(args) {
const q = this.getProvenTxsForUserQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const mp = (0, index_all_1.convertProofToMerklePath)(d.txid, {
index: d.index,
nodes: deserializeTscMerkleProofNodes(d.nodes),
height: d.height
});
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
provenTxId: (0, index_all_1.verifyInteger)(d.provenTxId),
txid: (0, index_all_1.verifyHexString)(d.txid),
height: (0, index_all_1.verifyInteger)(d.height),
index: (0, index_all_1.verifyInteger)(d.index),
merklePath: mp.toBinary(),
rawTx: Array.from((0, index_all_1.verifyTruthy)(d.rawTx)),
blockHash: (0, index_all_1.verifyHexString)((0, index_all_1.asString)((0, index_all_1.verifyTruthy)(d.blockHash))),
merkleRoot: (0, index_all_1.verifyHexString)((0, index_all_1.asString)((0, index_all_1.verifyTruthy)(d.merkleRoot))),
};
rs.push(r);
}
return this.validateEntities(rs);
}
getProvenTxReqsForUserQuery(args) {
const k = this.toDb(args.trx);
let q = k('proven_tx_reqs').where(function () {
this.whereExists(k.select('*').from('transactions').whereRaw(`proven_tx_reqs.txid = transactions.txid and transactions.userId = ${args.userId}`));
});
if (args.paged) {
q = q.limit(args.paged.limit);
q = q.offset(args.paged.offset || 0);
}
if (args.since)
q = q.where('updated_at', '>=', args.since);
return q;
}
async getProvenTxReqsForUser(args) {
const q = this.getProvenTxReqsForUserQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
provenTxReqId: (0, index_all_1.verifyInteger)(d.provenTxReqId),
provenTxId: verifyOptionalInteger(d.provenTxId),
txid: (0, index_all_1.verifyTruthy)(d.txid),
rawTx: Array.from((0, index_all_1.verifyTruthy)(d.rawTx)),
status: (0, index_all_1.verifyTruthy)(convertReqStatus(d.status)),
attempts: (0, index_all_1.verifyInteger)(d.attempts),
notified: !!d.notified,
history: (0, index_all_1.verifyTruthy)(d.history),
notify: (0, index_all_1.verifyTruthy)(d.notify),
inputBEEF: d.beef ? Array.from(d.beef) : undefined
};
rs.push(r);
}
return this.validateEntities(rs, undefined, ['notified']);
}
getTxLabelMapsForUserQuery(args) {
const k = this.toDb(args.trx);
let q = k('tx_labels_map')
.whereExists(k.select('*').from('tx_labels').whereRaw(`tx_labels.txLabelId = tx_labels_map.txLabelId and tx_labels.userId = ${args.userId}`));
if (args.since)
q = q.where('updated_at', '>=', this.validateDateForWhere(args.since));
if (args.paged) {
q = q.limit(args.paged.limit);
q = q.offset(args.paged.offset || 0);
}
return q;
}
async getTxLabelMapsForUser(args) {
const q = this.getTxLabelMapsForUserQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
txLabelId: (0, index_all_1.verifyInteger)(d.txLabelId),
transactionId: (0, index_all_1.verifyInteger)(d.transactionId),
isDeleted: !!d.isDeleted
};
rs.push(r);
}
return this.validateEntities(rs, undefined, ['isDeleted']);
}
getOutputTagMapsForUserQuery(args) {
const k = this.toDb(args.trx);
let q = k('output_tags_map')
.whereExists(k.select('*').from('output_tags').whereRaw(`output_tags.outputTagId = output_tags_map.outputTagId and output_tags.userId = ${args.userId}`));
if (args.since)
q = q.where('updated_at', '>=', this.validateDateForWhere(args.since));
if (args.paged) {
q = q.limit(args.paged.limit);
q = q.offset(args.paged.offset || 0);
}
return q;
}
async getOutputTagMapsForUser(args) {
const q = this.getOutputTagMapsForUserQuery(args);
const ds = await q;
const rs = [];
for (const d of ds) {
const r = {
created_at: (0, index_all_1.verifyTruthy)(d.created_at),
updated_at: (0, index_all_1.verifyTruthy)(d.updated_at),
outputId: (0, index_all_1.verifyInteger)(d.outputId),
outputTagId: (0, index_all_1.verifyInteger)(d.outputTagId),
isDeleted: !!d.isDeleted
};
rs.push(r);
}
return this.validateEntities(rs, undefined, ['isDeleted']);
}
countCertificateFields(args) {
throw new Error('Method not implemented.');
}
countCertificates(args) {
throw new Error('Method not implemented.');
}
countCommissions(args) {
throw new Error('Method not implemented.');
}
countOutputBaskets(args) {
throw new Error('Method not implemented.');
}
countOutputs(args) {
throw new Error('Method not implemented.');
}
countOutputTags(args) {
throw new Error('Method not implemented.');
}
countSyncStates(args) {
throw new Error('Method not implemented.');
}
countTransactions(args) {
throw new Error('Method not implemented.');
}
countTxLabels(args) {
throw new Error('Method not implemented.');
}
countUsers(args) {
throw new Error('Method not implemented.');
}
findMonitorEvents(args) {
throw new Error('Method not implemented.');
}
countMonitorEvents(args) {
throw new Error('Method not implemented.');
}
/**
* Helper to force uniform behavior across database engines.
* Use to process all individual records with time stamps retreived from database.
*/
validateEntity(entity, dateFields, booleanFields) {
entity.created_at = this.validateDate(entity.created_at);
entity.updated_at = this.validateDate(entity.updated_at);
if (dateFields) {
for (const df of dateFields) {
if (entity[df])
entity[df] = this.validateDate(entity[df]);
}
}
if (booleanFields) {
for (const df of booleanFields) {
if (entity[df] !== undefined)
entity[df] = !!(entity[df]);
}
}
for (const key of Object.keys(entity)) {
const val = entity[key];
if (val === null) {
entity[key] = undefined;
}
else if (Buffer.isBuffer(val)) {
entity[key] = Array.from(val);
}
}
return entity;
}
/**
* Helper to force uniform behavior across database engines.
* Use to process all arrays of records with time stamps retreived from database.
* @returns input `entities` array with contained values validated.
*/
validateEntities(entities, dateFields, booleanFields) {
for (let i = 0; i < entities.length; i++) {
entities[i] = this.validateEntity(entities[i], dateFields, booleanFields);
}
return entities;
}
}
exports.StorageMySQLDojoReader = StorageMySQLDojoReader;
function deserializeTscMerkleProofNodes(nodes) {
if (!Buffer.isBuffer(nodes))
throw new index_all_1.sdk.WERR_INTERNAL('Buffer or string expected.');
const buffer = nodes;
const ns = [];
for (let offset = 0; offset < buffer.length;) {
const flag = buffer[offset++];
if (flag === 1)
ns.push('*');
else if (flag === 0) {
ns.push((0, index_all_1.asString)(buffer.subarray(offset, offset + 32)));
offset += 32;
}
else {
throw new index_all_1.sdk.WERR_BAD_REQUEST(`node type byte ${flag} is not supported here.`);
}
}
return ns;
}
function convertReqStatus(status) {
return status;
}
//type TransactionStatus =
// 'completed' | 'failed' | 'unprocessed' | 'sending' | 'unproven' | 'unsigned' | 'nosend'
function convertTxStatus(status) {
return status;
}
function nullToUndefined(v) {
if (v === null)
return undefined;
if (typeof v === 'string')
return v.trim();
return v;
}
function verifyOptionalInteger(v) {
if (v === undefined || v === null)
return undefined;
if (typeof v !== 'number' || !Number.isInteger(v))
throw new index_all_1.sdk.WERR_INTERNAL('An integer is required.');
return v;
}
function convertSyncStatus(status) {
return status;
}
function forceToBase64(s) {
if (!s)
return (0, index_all_1.randomBytesBase64)(12);
if ((0, sdk_2.isHexString)(s))
return sdk_1.Utils.toBase64((0, index_all_1.asArray)(s.trim()));
return s.trim();
}
//# sourceMappingURL=StorageMySQLDojoReader.js.map