@bsv/overlay
Version:
BSV Blockchain Overlay Services Engine
217 lines • 8.78 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.KnexStorage = void 0;
class KnexStorage {
constructor(knex) {
this.knex = knex;
}
async findOutput(txid, outputIndex, topic, spent, includeBEEF = false) {
const search = {
'outputs.txid': txid,
'outputs.outputIndex': outputIndex
};
if (topic !== undefined)
search['outputs.topic'] = topic;
if (spent !== undefined)
search['outputs.spent'] = spent;
// Base query to get the output
const query = this.knex('outputs').where(search);
// Select necessary fields from outputs and conditionally include beef from transactions
const selectFields = [
'outputs.txid',
'outputs.outputIndex',
'outputs.outputScript',
'outputs.topic',
'outputs.satoshis',
'outputs.outputsConsumed',
'outputs.spent',
'outputs.consumedBy',
'outputs.score'
];
if (includeBEEF) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
query.leftJoin('transactions', 'outputs.txid', 'transactions.txid');
selectFields.push('transactions.beef');
}
const output = await query.select(selectFields).first();
if (output === undefined || output === null) {
return null;
}
return {
...output,
outputScript: [...output.outputScript],
beef: includeBEEF ? (output.beef !== undefined ? [...output.beef] : undefined) : undefined,
spent: Boolean(output.spent),
outputsConsumed: JSON.parse(output.outputsConsumed),
consumedBy: JSON.parse(output.consumedBy)
};
}
async findOutputsForTransaction(txid, includeBEEF = false) {
// Base query to get outputs
const query = this.knex('outputs').where({ 'outputs.txid': txid });
// Select necessary fields from outputs and conditionally include beef from transactions
const selectFields = [
'outputs.txid',
'outputs.outputIndex',
'outputs.outputScript',
'outputs.topic',
'outputs.satoshis',
'outputs.outputsConsumed',
'outputs.spent',
'outputs.consumedBy',
'outputs.score'
];
if (includeBEEF) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
query.leftJoin('transactions', 'outputs.txid', 'transactions.txid');
selectFields.push('transactions.beef');
}
const outputs = await query.select(selectFields);
if (outputs === undefined || outputs.length === 0) {
return [];
}
return outputs.map(output => ({
...output,
outputScript: [...output.outputScript],
beef: includeBEEF ? (output.beef !== undefined ? [...output.beef] : undefined) : undefined,
spent: Boolean(output.spent),
outputsConsumed: JSON.parse(output.outputsConsumed),
consumedBy: JSON.parse(output.consumedBy)
}));
}
async findUTXOsForTopic(topic, since, limit, includeBEEF = false) {
// Base query to get outputs
const query = this.knex('outputs').where({ 'outputs.topic': topic, 'outputs.spent': false });
// If provided, additionally filters UTXOs by score
if (since !== undefined && since > 0) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
query.andWhere('outputs.score', '>=', since);
}
// Sort by score
// eslint-disable-next-line @typescript-eslint/no-floating-promises
query.orderBy('outputs.score', 'asc');
// Select necessary fields from outputs and conditionally include beef from transactions
const selectFields = [
'outputs.txid',
'outputs.outputIndex',
'outputs.outputScript',
'outputs.topic',
'outputs.satoshis',
'outputs.outputsConsumed',
'outputs.spent',
'outputs.consumedBy',
'outputs.score'
];
if (includeBEEF) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
query.leftJoin('transactions', 'outputs.txid', 'transactions.txid');
selectFields.push('transactions.beef');
}
// Apply limit if specified
if (limit !== undefined && limit > 0) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
query.limit(limit);
}
const outputs = await query.select(selectFields);
if (outputs === undefined || outputs.length === 0) {
return [];
}
return outputs.map(output => ({
...output,
outputScript: [...output.outputScript],
beef: includeBEEF ? (output.beef !== undefined ? [...output.beef] : undefined) : undefined,
spent: Boolean(output.spent),
outputsConsumed: JSON.parse(output.outputsConsumed),
consumedBy: JSON.parse(output.consumedBy)
}));
}
async deleteOutput(txid, outputIndex, _) {
await this.knex.transaction(async (trx) => {
// Delete the specific output
await trx('outputs').where({ txid, outputIndex }).del();
// Check how many outputs reference the same transaction
const remainingOutputs = await trx('outputs').where({ txid }).count('* as count').first();
if (remainingOutputs !== undefined && Number(remainingOutputs.count) === 0) {
// If no more outputs reference the transaction, delete the beef
await trx('transactions').where({ txid }).del();
}
});
}
async insertOutput(output) {
const insertPromises = [this.knex('outputs').insert({
txid: output.txid,
outputIndex: Number(output.outputIndex),
outputScript: Buffer.from(output.outputScript),
topic: output.topic,
satoshis: Number(output.satoshis),
outputsConsumed: JSON.stringify(output.outputsConsumed),
consumedBy: JSON.stringify(output.consumedBy),
spent: output.spent,
score: output.score
})];
if (output.beef !== undefined) {
const insertTransactionPromise = this.knex('transactions').insert({
txid: output.txid,
beef: Buffer.from(output.beef)
}).onConflict('txid').ignore();
insertPromises.push(insertTransactionPromise);
}
await this.knex.transaction(async (trx) => {
await Promise.all(insertPromises.map(promise => promise.transacting(trx)));
});
}
async markUTXOAsSpent(txid, outputIndex, topic) {
await this.knex('outputs').where({
txid,
outputIndex,
topic
}).update('spent', true);
}
async updateConsumedBy(txid, outputIndex, topic, consumedBy) {
await this.knex('outputs').where({
txid,
outputIndex,
topic
}).update('consumedBy', JSON.stringify(consumedBy));
}
async updateTransactionBEEF(txid, beef) {
await this.knex('transactions').where({
txid
}).update('beef', Buffer.from(beef));
}
async updateOutputBlockHeight(txid, outputIndex, topic, blockHeight) {
await this.knex('outputs').where({
txid,
outputIndex,
topic
}).update('blockHeight', blockHeight);
}
async insertAppliedTransaction(tx) {
await this.knex('applied_transactions').insert({
txid: tx.txid,
topic: tx.topic
});
}
async doesAppliedTransactionExist(tx) {
const result = await this.knex('applied_transactions')
.where({ txid: tx.txid, topic: tx.topic })
.select(this.knex.raw('1'))
.first();
return !!result;
}
async updateLastInteraction(host, topic, since) {
await this.knex('host_sync_state')
.insert({ host, topic, since })
.onConflict(['host', 'topic'])
.merge({ since });
}
async getLastInteraction(host, topic) {
const result = await this.knex('host_sync_state')
.where({ host, topic })
.select('since')
.first();
return result ? result.since : 0;
}
}
exports.KnexStorage = KnexStorage;
//# sourceMappingURL=KnexStorage.js.map