mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-10 02:28:13 -07:00
website_next: part 9
This commit is contained in:
@@ -1,43 +1,237 @@
|
||||
import { brk } from "../../../utils/client.js";
|
||||
import { FILTERS, getFilterBit, getTypeBit } from "./filters/model.js";
|
||||
|
||||
const VERSION_BITS = new Map([
|
||||
[1, getFilterBit("version:1")],
|
||||
[2, getFilterBit("version:2")],
|
||||
[3, getFilterBit("version:3")],
|
||||
]);
|
||||
|
||||
const RBF_YES_BIT = getFilterBit("rbf:yes");
|
||||
const RBF_NO_BIT = getFilterBit("rbf:no");
|
||||
const INPUT_ONE_BIT = getFilterBit("input:one");
|
||||
const INPUT_MULTI_BIT = getFilterBit("input:multi");
|
||||
const OUTPUT_ONE_BIT = getFilterBit("output:one");
|
||||
const OUTPUT_MULTI_BIT = getFilterBit("output:multi");
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {{
|
||||
* slice: (
|
||||
* start: number,
|
||||
* end: number,
|
||||
* ) => {
|
||||
* fetch: (
|
||||
* options: { signal: AbortSignal, memCache: false },
|
||||
* ) => Promise<{ data: T[] }>,
|
||||
* },
|
||||
* }} series
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @param {AbortSignal} signal
|
||||
* @returns {Promise<{ data: T[] }>}
|
||||
*/
|
||||
function fetchSeriesSlice(series, start, end, signal) {
|
||||
return series.slice(start, end).fetch({ signal, memCache: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../../../modules/brk-client/index.js").BlockInfoV1} block
|
||||
* @param {AbortSignal} signal
|
||||
*/
|
||||
export async function loadBlockPreview(block) {
|
||||
async function loadBlockPreviewRange(block, signal) {
|
||||
const firstTxIndex = (
|
||||
await brk.series.transactions.raw.firstTxIndex.by.height
|
||||
.get(block.height)
|
||||
.fetch()
|
||||
await fetchSeriesSlice(
|
||||
brk.series.transactions.raw.firstTxIndex.by.height,
|
||||
block.height,
|
||||
block.height + 1,
|
||||
signal,
|
||||
)
|
||||
).data[0];
|
||||
|
||||
signal.throwIfAborted();
|
||||
|
||||
const start = /** @type {number} */ (firstTxIndex);
|
||||
const end = start + block.txCount;
|
||||
|
||||
return { start, end };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../../../modules/brk-client/index.js").BlockInfoV1} block
|
||||
* @param {AbortSignal} signal
|
||||
*/
|
||||
export async function loadBlockPreview(block, signal) {
|
||||
const { start, end } = await loadBlockPreviewRange(block, signal);
|
||||
const tx = brk.series.transactions;
|
||||
const [weights, feeRates] = await Promise.all([
|
||||
fetchSeriesSlice(tx.size.weight.txIndex.by.tx_index, start, end, signal),
|
||||
fetchSeriesSlice(tx.fees.feeRate.by.tx_index, start, end, signal),
|
||||
]);
|
||||
|
||||
signal.throwIfAborted();
|
||||
|
||||
return {
|
||||
range: { start, end },
|
||||
transactions: weights.data.map((weight, index) => ({
|
||||
txIndex: start + index,
|
||||
weight: /** @type {number} */ (weight),
|
||||
feeRate: /** @type {number} */ (feeRates.data[index]),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} txIndex
|
||||
* @param {AbortSignal} signal
|
||||
*/
|
||||
export async function loadBlockPreviewTxid(txIndex, signal) {
|
||||
const txid = (
|
||||
await brk.series.transactions.raw.txid.by.tx_index
|
||||
.get(txIndex)
|
||||
.fetch({ signal, memCache: false })
|
||||
).data[0];
|
||||
|
||||
signal.throwIfAborted();
|
||||
|
||||
return /** @type {string} */ (txid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number[]} starts
|
||||
* @param {number[]} counts
|
||||
*/
|
||||
function rangeEnd(starts, counts) {
|
||||
const last = starts.length - 1;
|
||||
|
||||
return starts[last] + counts[last];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint32Array} counts
|
||||
* @param {number} mask
|
||||
*/
|
||||
function countMask(counts, mask) {
|
||||
for (const { bit, index } of FILTERS) {
|
||||
if (mask & bit) counts[index] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {readonly string[]} types
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
*/
|
||||
function getTypesMask(types, start, end) {
|
||||
let mask = 0;
|
||||
|
||||
for (let index = start; index < end; index += 1) {
|
||||
mask |= getTypeBit(types[index]);
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BlockPreviewRange} range
|
||||
* @param {AbortSignal} signal
|
||||
*/
|
||||
export async function loadBlockPreviewFilters(range, signal) {
|
||||
const { start, end } = range;
|
||||
const tx = brk.series.transactions;
|
||||
const indexes = brk.series.indexes.txIndex;
|
||||
const [
|
||||
txids,
|
||||
versions,
|
||||
weights,
|
||||
feeRates,
|
||||
rbfs,
|
||||
inputCounts,
|
||||
outputCounts,
|
||||
firstInputs,
|
||||
firstOutputs,
|
||||
] = await Promise.all([
|
||||
tx.raw.txid.by.tx_index.slice(start, end).fetch(),
|
||||
tx.raw.txVersion.by.tx_index.slice(start, end).fetch(),
|
||||
tx.size.weight.txIndex.by.tx_index.slice(start, end).fetch(),
|
||||
tx.fees.feeRate.by.tx_index.slice(start, end).fetch(),
|
||||
tx.raw.isExplicitlyRbf.by.tx_index.slice(start, end).fetch(),
|
||||
indexes.inputCount.by.tx_index.slice(start, end).fetch(),
|
||||
indexes.outputCount.by.tx_index.slice(start, end).fetch(),
|
||||
fetchSeriesSlice(tx.raw.txVersion.by.tx_index, start, end, signal),
|
||||
fetchSeriesSlice(tx.raw.isExplicitlyRbf.by.tx_index, start, end, signal),
|
||||
fetchSeriesSlice(indexes.inputCount.by.tx_index, start, end, signal),
|
||||
fetchSeriesSlice(indexes.outputCount.by.tx_index, start, end, signal),
|
||||
fetchSeriesSlice(tx.raw.firstTxinIndex.by.tx_index, start, end, signal),
|
||||
fetchSeriesSlice(tx.raw.firstTxoutIndex.by.tx_index, start, end, signal),
|
||||
]);
|
||||
|
||||
return txids.data.map((txid, index) => ({
|
||||
txid,
|
||||
version: versions.data[index],
|
||||
weight: weights.data[index],
|
||||
feeRate: feeRates.data[index],
|
||||
rbf: rbfs.data[index],
|
||||
inputCount: inputCounts.data[index],
|
||||
outputCount: outputCounts.data[index],
|
||||
}));
|
||||
signal.throwIfAborted();
|
||||
|
||||
const inputStart = /** @type {number} */ (firstInputs.data[0]);
|
||||
const outputStart = /** @type {number} */ (firstOutputs.data[0]);
|
||||
const [inputTypes, outputTypes] = await Promise.all([
|
||||
fetchSeriesSlice(
|
||||
brk.series.inputs.raw.outputType.by.txin_index,
|
||||
inputStart,
|
||||
rangeEnd(
|
||||
/** @type {number[]} */ (firstInputs.data),
|
||||
/** @type {number[]} */ (inputCounts.data),
|
||||
),
|
||||
signal,
|
||||
),
|
||||
fetchSeriesSlice(
|
||||
brk.series.outputs.raw.outputType.by.txout_index,
|
||||
outputStart,
|
||||
rangeEnd(
|
||||
/** @type {number[]} */ (firstOutputs.data),
|
||||
/** @type {number[]} */ (outputCounts.data),
|
||||
),
|
||||
signal,
|
||||
),
|
||||
]);
|
||||
|
||||
signal.throwIfAborted();
|
||||
|
||||
const masks = new Uint32Array(end - start);
|
||||
const counts = new Uint32Array(FILTERS.length);
|
||||
|
||||
versions.data.forEach((version, index) => {
|
||||
const inputCount = /** @type {number} */ (inputCounts.data[index]);
|
||||
const outputCount = /** @type {number} */ (outputCounts.data[index]);
|
||||
const inputTypeStart = /** @type {number} */ (firstInputs.data[index]);
|
||||
const inputTypeEnd = inputTypeStart + inputCount;
|
||||
const outputTypeStart = /** @type {number} */ (firstOutputs.data[index]);
|
||||
const outputTypeEnd = outputTypeStart + outputCount;
|
||||
const mask =
|
||||
(VERSION_BITS.get(/** @type {number} */ (version)) ?? 0) |
|
||||
(rbfs.data[index] ? RBF_YES_BIT : RBF_NO_BIT) |
|
||||
(inputCount === 1 ? INPUT_ONE_BIT : INPUT_MULTI_BIT) |
|
||||
(outputCount === 1 ? OUTPUT_ONE_BIT : OUTPUT_MULTI_BIT) |
|
||||
getTypesMask(
|
||||
/** @type {string[]} */ (inputTypes.data),
|
||||
inputTypeStart - inputStart,
|
||||
inputTypeEnd - inputStart,
|
||||
) |
|
||||
getTypesMask(
|
||||
/** @type {string[]} */ (outputTypes.data),
|
||||
outputTypeStart - outputStart,
|
||||
outputTypeEnd - outputStart,
|
||||
);
|
||||
|
||||
masks[index] = mask;
|
||||
countMask(counts, mask);
|
||||
});
|
||||
|
||||
return { counts, masks, start };
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} BlockPreviewRange
|
||||
* @property {number} start
|
||||
* @property {number} end
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} BlockPreviewTransaction
|
||||
* @property {number} txIndex
|
||||
* @property {number} weight
|
||||
* @property {number} feeRate
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} BlockPreviewFilterState
|
||||
* @property {number} start
|
||||
* @property {Uint32Array} masks
|
||||
* @property {Uint32Array} counts
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user