A new lineage/v0 op binds a node's keys in one inscription whose parent is the node itself: its Taproot and SegWit addresses, its npub and its Ethereum address. Every key signs the same statement the way its own wallets sign a message: BIP-322 simple signatures for the Bitcoin addresses, a NIP-01 note (kind 1, created_at 0) for the npub, EIP-191 for Ethereum. The reducer checks every proof, binds a standing keyring's keys to its node, lets the newest keyring replace the older, and retires keyrings at a graduation (the keys change with the new root) and at a close. Every account gets an Ethereum key at m/44'/60'/account'/0/0; an org's, at account 0, is the key tree page's legal-control key and matches its published vector. Keyring is the ninth verb; Inspect shows each node's keyring and the statement its keys signed, and Reveal its Ethereum private key. The sample publishes three keyrings, and lineage's retires when it graduates. Tests pin BIP-322's published vectors and values cross-checked with bip322-js, ethers and nostr-tools. The regtest rehearsal, rerun with five keyrings, confirmed 89 transactions on Bitcoin Core; ord's 26 events fold to the same trees and keyrings, every proof checked from ord's copy, and the fixture is re-recorded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4upF5LVwmPDDqrhdUfnXh
32 lines
2.5 KiB
JavaScript
32 lines
2.5 KiB
JavaScript
#!/usr/bin/env node
|
|
// lineage-index: read lineage/v0 events from an ord server and print the trees they imply, with the same reducer the
|
|
// lab uses. --compare <lab.json> checks that ord and a lab backup tell the same story (tools/regtest/README.md).
|
|
// node bin/lineage-index.mjs [--ord http://127.0.0.1:8088] [--from <height>] [--compare <lab.json>]
|
|
import { readFileSync } from 'node:fs';
|
|
import { parseArgs } from 'node:util';
|
|
import { decodeLab } from '../src/orgchain/store.js';
|
|
import { census, rows, summary } from '../src/orgchain/lineage.js';
|
|
import { ordClient, ordEvents, ordState } from '../src/indexer/ord.js';
|
|
import { compareWithLab } from '../src/indexer/compare.js';
|
|
|
|
const { values } = parseArgs({ options: { ord: { type: 'string', default: 'http://127.0.0.1:8088' }, from: { type: 'string', default: '0' },
|
|
compare: { type: 'string' } } });
|
|
const events = await ordEvents(ordClient(values.ord), { from: Number(values.from) });
|
|
const state = ordState(events), plural = (n, w) => `${n} ${w}${n === 1 ? '' : 's'}`;
|
|
for (const root of state.roots) {
|
|
const c = census(state, root.id);
|
|
console.log(`\n${root.name} · ${plural(c.orgs, 'org')}, ${plural(c.agents, 'agent')}, ${plural(c.belts, 'belt')}, ${plural(c.decisions, 'decision')}`);
|
|
console.log(rows(state, root.id).map(r => (r.type === 'group' ? `${r.guide}${r.label}` : `${r.guide}${r.path} ${r.node.name}${r.away ? ' · graduated' : ''}`)).join('\n'));
|
|
}
|
|
const rejected = state.log.filter(e => !e.ok);
|
|
console.log(`\n${plural(events.length, 'lineage/v0 event')} on ord, ${plural(state.items.size, 'item')}${rejected.length ? `, ${rejected.length} rejected:` : ''}`);
|
|
for (const e of rejected) console.log(` ${summary(e, state)} (${e.id})`);
|
|
const rings = [...state.nodes.values()].filter(n => n.keyring && !n.keyring.retired);
|
|
if (rings.length) console.log(`${plural(rings.length, 'keyring')} bind ${plural(state.bound.size, 'key')}: ${rings.map(n => n.name).join(', ')}`);
|
|
if (values.compare) {
|
|
const { problems, ...n } = compareWithLab(decodeLab(readFileSync(values.compare, 'utf8')), events);
|
|
console.log(problems.length ? `\nord and the lab differ:\n${problems.map(p => ` ${p}`).join('\n')}`
|
|
: `\nSame story as ${values.compare}: ${plural(n.events, "event")}, ${plural(n.nodes, "node")} and ${plural(n.items, "item")}, each event in the same beat and every inscription in the same output.${n.reordered ? ` The miner ordered ${n.reordered} independent events differently within their blocks, which changes nothing.` : ""}`);
|
|
process.exitCode = problems.length ? 1 : 0;
|
|
}
|