mempool/backend/src/repositories/CpfpRepository.ts

145 lines
4.3 KiB
TypeScript
Raw Normal View History

2023-01-09 10:24:12 -06:00
import cluster, { Cluster } from 'cluster';
import { RowDataPacket } from 'mysql2';
2022-11-27 13:46:23 +09:00
import DB from '../database';
import logger from '../logger';
import { Ancestor } from '../mempool.interfaces';
2023-01-09 10:24:12 -06:00
import transactionRepository from '../repositories/TransactionRepository';
2022-11-27 13:46:23 +09:00
class CpfpRepository {
2023-01-09 10:24:12 -06:00
public async $saveCluster(clusterRoot: string, height: number, txs: Ancestor[], effectiveFeePerVsize: number): Promise<void> {
if (!txs[0]) {
return;
}
2022-11-27 13:46:23 +09:00
try {
2023-01-09 10:24:12 -06:00
const packedTxs = Buffer.from(this.pack(txs));
2022-11-27 13:46:23 +09:00
await DB.query(
`
2023-01-09 10:24:12 -06:00
INSERT INTO compact_cpfp_clusters(root, height, txs, fee_rate)
VALUE (UNHEX(?), ?, ?, ?)
2022-11-27 13:46:23 +09:00
ON DUPLICATE KEY UPDATE
height = ?,
txs = ?,
fee_rate = ?
`,
2023-01-09 10:24:12 -06:00
[clusterRoot, height, packedTxs, effectiveFeePerVsize, height, packedTxs, effectiveFeePerVsize]
2022-11-27 13:46:23 +09:00
);
2023-01-09 10:24:12 -06:00
for (const tx of txs) {
await transactionRepository.$setCluster(tx.txid, clusterRoot);
}
2022-11-27 13:46:23 +09:00
} catch (e: any) {
logger.err(`Cannot save cpfp cluster into db. Reason: ` + (e instanceof Error ? e.message : e));
throw e;
}
}
2023-01-09 10:24:12 -06:00
public async $getCluster(clusterRoot: string): Promise<Cluster> {
const [clusterRows]: any = await DB.query(
`
SELECT *
FROM compact_cpfp_clusters
WHERE root = UNHEX(?)
`,
[clusterRoot]
);
const cluster = clusterRows[0];
cluster.txs = this.unpack(cluster.txs);
return cluster;
}
2022-11-27 13:46:23 +09:00
public async $deleteClustersFrom(height: number): Promise<void> {
logger.info(`Delete newer cpfp clusters from height ${height} from the database`);
try {
2023-01-09 10:24:12 -06:00
const [rows] = await DB.query(
`
SELECT txs, height, root from compact_cpfp_clusters
WHERE height >= ?
`,
[height]
) as RowDataPacket[][];
if (rows?.length) {
for (let clusterToDelete of rows) {
const txs = this.unpack(clusterToDelete.txs);
for (let tx of txs) {
await transactionRepository.$removeTransaction(tx.txid);
}
}
}
2022-11-27 13:46:23 +09:00
await DB.query(
`
2023-01-09 10:24:12 -06:00
DELETE from compact_cpfp_clusters
2022-11-27 13:46:23 +09:00
WHERE height >= ?
`,
[height]
);
} catch (e: any) {
logger.err(`Cannot delete cpfp clusters from db. Reason: ` + (e instanceof Error ? e.message : e));
throw e;
}
}
2023-01-09 10:24:12 -06:00
// insert a dummy row to mark that we've indexed as far as this block
public async $insertProgressMarker(height: number): Promise<void> {
try {
const [rows]: any = await DB.query(
`
SELECT root
FROM compact_cpfp_clusters
WHERE height = ?
`,
[height]
);
if (!rows?.length) {
const rootBuffer = Buffer.alloc(32);
rootBuffer.writeInt32LE(height);
await DB.query(
`
INSERT INTO compact_cpfp_clusters(root, height, fee_rate)
VALUE (?, ?, ?)
`,
[rootBuffer, height, 0]
);
}
} catch (e: any) {
logger.err(`Cannot insert cpfp progress marker. Reason: ` + (e instanceof Error ? e.message : e));
throw e;
}
}
2023-01-09 10:24:12 -06:00
public pack(txs: Ancestor[]): ArrayBuffer {
const buf = new ArrayBuffer(44 * txs.length);
const view = new DataView(buf);
txs.forEach((tx, i) => {
const offset = i * 44;
for (let x = 0; x < 32; x++) {
// store txid in little-endian
view.setUint8(offset + (31 - x), parseInt(tx.txid.slice(x * 2, (x * 2) + 2), 16));
}
view.setUint32(offset + 32, tx.weight);
view.setBigUint64(offset + 36, BigInt(Math.round(tx.fee)));
});
return buf;
}
public unpack(buf: Buffer): Ancestor[] {
if (!buf) {
return [];
}
const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
const txs: Ancestor[] = [];
const view = new DataView(arrayBuffer);
for (let offset = 0; offset < arrayBuffer.byteLength; offset += 44) {
const txid = Array.from(new Uint8Array(arrayBuffer, offset, 32)).reverse().map(b => b.toString(16).padStart(2, '0')).join('');
const weight = view.getUint32(offset + 32);
const fee = Number(view.getBigUint64(offset + 36));
txs.push({
txid,
weight,
fee
});
}
return txs;
}
2022-11-27 13:46:23 +09:00
}
export default new CpfpRepository();