Files
orgchain/src/adapters/sats.js
T
teahubxyzandClaude Opus 5.5 7c9306402d Orgchain v1.9: lineage/v0 as an ordinals metaprotocol, rehearsed on regtest
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
2026-10-15 02:04:35 -07:00

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; });
}