SPEC.md writes lineage/v0 down as an ordinals metaprotocol: the envelope fields, chain order, authority by verified parents, every rule, ownership by sat. bin/lineage-index.mjs reads lineage events from a real ord server and folds them with the same reducer the lab uses. tools/regtest rehearses the sample on Bitcoin Core regtest with standardness on: the lab's own action code signs on a chain funded only by coins imported from regtest (LocalChain.importCoin), each beat's transactions are broadcast byte for byte and mined, ord indexes them, and --compare checks the same events in the same beats, the same metadata and parents, every inscription in the same output and the same trees. tests/indexer.test.js replays a recorded run. The first rehearsal found that reveals committed to tapscript's re-encoded tapleaf, which real Bitcoin rejects. They now commit to the exact script bytes, and the local chain checks script paths per BIP-341/342, still loading reveals made before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4upF5LVwmPDDqrhdUfnXh
30 lines
1.5 KiB
JavaScript
30 lines
1.5 KiB
JavaScript
// Synthetic local numbering: each height issues 5e9 consecutive sats. Fees retire
|
|
// the unassigned input tail (not Bitcoin miner fee collection). Half-open ranges.
|
|
export const LOCAL_REWARD = 5e9;
|
|
// Orgchain: coins imported from a real chain (LocalChain.importCoin) number their sats from 2^52 up, one 5e9 block of
|
|
// numbers each, well clear of every local coinbase's.
|
|
export const IMPORT_BASE = 2 ** 52;
|
|
export const importRanges = (index, value) => [[IMPORT_BASE + index * LOCAL_REWARD, IMPORT_BASE + index * LOCAL_REWARD + value]];
|
|
export function sliceRanges(ranges, offset, length) {
|
|
const result = [];
|
|
for (const [start, end] of ranges) {
|
|
const size = end - start;
|
|
if (offset >= size) { offset -= size; continue; }
|
|
const take = Math.min(length, size - offset);
|
|
if (take) result.push([start + offset, start + offset + take]);
|
|
length -= take; offset = 0; if (!length) break;
|
|
}
|
|
return result;
|
|
}
|
|
export function satAt(ranges, offset) { return sliceRanges(ranges, offset, 1)[0]?.[0]; }
|
|
export function offsetOf(ranges, sat) {
|
|
let offset = 0;
|
|
for (const [start, end] of ranges) { if (sat >= start && sat < end) return offset + sat - start; offset += end - start; }
|
|
return null;
|
|
}
|
|
export function outputRanges(tx, coins, height, coinbase) {
|
|
const ranges = coinbase ? [[(height - 1) * LOCAL_REWARD, height * LOCAL_REWARD]] : coins.flatMap(c => c.ranges);
|
|
let offset = 0;
|
|
return tx.vout.map(out => { const result = sliceRanges(ranges, offset, Number(out.value)); offset += Number(out.value); return result; });
|
|
}
|