Files
orgchain/tests/charter.test.js
T
teahubxyzandClaude Opus 5.5 74ed801176 Orgchain v1.13: whole trees change hands
An org now changes hands with its tree, the derived orgs and agents below
it and the items they hold, and a holding changes hands whole, sold by its
owner, whom the lab wallet plays (the price goes there). Sales stay
cash-free: the money in the tree, every gas tank included, goes to the
seller first, and the items go with the tree.

An ask signs one input, so listing a tree bundles every inscription in it
into one output at the lot's key; an offer spends every output the tree sits
in and merges them. After the join and the adopt, which now move a holding
too (it becomes its buyer's next org), the buyer splits the output back, one
inscription to an output at its new key, each item with the node that held
it, by the layout the deal recorded. A listed or sold tree is frozen whole:
nothing in it acts, sends, is funded or sells alone, nothing above it
graduates or closes it, and a listed holding mines nothing. Graduation roots
are kept by graduation event, since a node can graduate, move and graduate
again. Transactions take up to 64 inputs and outputs.

Rehearsed on Bitcoin Core regtest from height 144: sixteen beats and 131
transactions, time with its six below it bought on an offer, and acme sold
whole on an ask, eleven inscriptions in one output, then split again; ord
puts every inscription in the same output as the lab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4upF5LVwmPDDqrhdUfnXh
2026-10-15 14:51:52 -07:00

102 lines
7.8 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { PROTOCOL, isSeat, reduce, standing, summary } from '../src/orgchain/lineage.js';
import { emptyLab, setTempo } from '../src/orgchain/store.js';
import { act, lineageState, send } from '../src/orgchain/actions.js';
import { holderOf, keyOf, secretsOf } from '../src/orgchain/keys.js';
import { SAT, payerOf } from '../src/orgchain/money.js';
let n = 0;
const ev = (op, parent = null, meta = {}, height = 1) => ({ id: `${String(++n).padStart(64, '0')}i0`, status: height === null ? 'pending' : 'confirmed',
height, metadata: { op, ...meta }, parent, protocol: PROTOCOL });
const charter = (by, meta, height = 2) => ev('charter', by.id, meta, height);
test('a charter records a legal form: a node charters itself, and a series is chartered by its master', () => {
const root = ev('found', null, { name: 'acme' }), ops = ev('birth', root.id, { name: 'ops' }), lab = ev('birth', ops.id, { name: 'lab' }),
bot = ev('birth', ops.id, { name: 'bot', kind: 'agent' }), other = ev('found', null, { name: 'other' });
const filing = 'ab'.repeat(32), tries = [
[charter(root, { form: 'series', of: ops.id }), 'acme is not chartered as a series LLC'],
[charter(root, { form: 'series-llc', jurisdiction: 'US-WY', legal: 'Acme Holdings LLC', provider: 'doola', filing }), 'ok'],
[charter(root, { form: 'series', of: ops.id }), 'ok'],
[charter(root, { form: 'series', of: lab.id }), 'ok'],
[charter(root, { form: 'series', of: bot.id }), 'only orgs become series'],
[charter(other, { form: 'series', of: ops.id }), 'a series must descend from its master'],
[charter(root, { form: 'series', of: ops.id, jurisdiction: 'US-DE' }), "a series takes its master's jurisdiction"],
[charter(ops, { form: 'llc' }), 'a jurisdiction is an ISO code such as US-WY'],
[charter(ops, { form: 'llc', jurisdiction: 'wyoming' }), 'a jurisdiction is an ISO code such as US-WY'],
[charter(ops, { form: 'sole' }), 'a sole proprietor is a person: a holding or an agent'],
[charter(bot, { form: 'llc', jurisdiction: 'US-DE' }), 'an agent graduates before it incorporates'],
[charter(bot, { form: 'sole' }), 'ok'],
[charter(ops, { form: 'gmbh', jurisdiction: 'DE' }), "a charter's form is sole, llc, series-llc, series, corp"],
[charter(ops, { form: 'llc', jurisdiction: 'US-WY', filing: 'nope' }), 'a filing is the SHA-256 of the filed documents, in hex'],
[charter(ops, { form: 'llc', jurisdiction: 'US-WY', provider: 'Doola Inc' }), /^a provider's name follows the name rule/],
[charter(ops, { form: 'llc', jurisdiction: 'US-WY', of: root.id }), 'a node charters itself; only a series is chartered by its master'],
];
const s = reduce([root, ops, lab, bot, other, ...tries.map(([e]) => e)]);
tries.forEach(([e, want]) => {
const entry = s.log.find(x => x.id === e.id), got = entry.ok ? 'ok' : entry.why;
if (want instanceof RegExp) assert.match(got, want); else assert.equal(got, want);
});
const node = e => s.nodes.get(e.id);
assert.deepEqual(node(root).charter, { id: tries[1][0].id, beat: 2, form: 'series-llc', jurisdiction: 'US-WY', legal: 'Acme Holdings LLC',
provider: 'doola', filing, master: null });
assert.deepEqual([node(ops).charter.form, node(ops).charter.master, node(lab).charter.master], ['series', root.id, root.id]);
assert.equal(node(bot).charter.form, 'sole');
assert.deepEqual([ops, lab, bot].map(e => standing(s, node(e))), [{ ok: true, why: '' }, { ok: true, why: '' }, { ok: true, why: '' }]);
assert.equal(summary(s.log.find(x => x.id === tries[1][0].id), s), 'charter acme · series LLC (US-WY)');
assert.equal(summary(s.log.find(x => x.id === tries[2][0].id), s), 'charter ops as a series of acme');
});
test("a series' standing follows the tree: graduating, its master dropping series, or a close lapses it, in any order", () => {
const root = ev('found', null, { name: 'acme' }), ops = ev('birth', root.id, { name: 'ops' }), lab = ev('birth', ops.id, { name: 'lab' });
const master = charter(root, { form: 'series-llc', jurisdiction: 'US-WY' }), series = charter(root, { form: 'series', of: ops.id }, 3);
const kid = charter(root, { form: 'series', of: lab.id }, 3), grad = ev('graduate', root.id, { of: ops.id }, 3);
const base = [root, ops, lab, master];
for (const s of [reduce([...base, series, kid, grad]), reduce([...base, grad, series, kid])]) {
assert.equal(s.nodes.get(ops.id).charter.form, 'series', 'a graduate keeps its parent, so it still descends from its master');
assert.deepEqual(standing(s, s.nodes.get(ops.id)), { ok: false, why: "ops is no longer in acme's holding" });
assert.deepEqual(standing(s, s.nodes.get(lab.id)), { ok: false, why: "lab is no longer in acme's holding" });
}
// Once it graduates, ops charters itself as an LLC, and that stands.
const own = reduce([...base, series, grad, charter(ops, { form: 'llc', jurisdiction: 'US-DE', legal: 'Ops LLC' }, 4)]);
assert.deepEqual(standing(own, own.nodes.get(ops.id)), { ok: true, why: '' });
// The master drops series, so its series lapse; a closed series is closed.
const s = reduce([...base, series, kid, charter(root, { form: 'llc', jurisdiction: 'US-WY' }, 4), ev('close', lab.id, {}, 4)]);
assert.deepEqual(standing(s, s.nodes.get(ops.id)), { ok: false, why: 'acme no longer holds series' });
assert.deepEqual(standing(s, s.nodes.get(lab.id)), { ok: false, why: 'lab is closed' });
});
test('a seat graduates into a holding of its own, and can then birth, award and incorporate', () => {
const root = ev('found', null, { name: 'guild' }), ada = ev('birth', root.id, { name: 'ada', kind: 'agent' }), bo = ev('birth', root.id, { name: 'bo', kind: 'agent' });
const s = reduce([root, ada, bo, ev('graduate', root.id, { of: ada.id }, 2), ev('birth', ada.id, { name: 'studio' }, 3),
ev('award', ada.id, { to: bo.id, skill: 'pair', belt: 'white' }, 3), charter(ada, { form: 'llc', jurisdiction: 'US-WY', legal: 'Ada LLC' }, 3)]);
assert.ok(s.log.every(e => e.ok), s.log.filter(e => !e.ok).map(e => e.why).join('; '));
const node = s.nodes.get(ada.id);
assert.deepEqual([node.kind, node.edge, node.root === node.id, isSeat(node), isSeat(s.nodes.get(bo.id))], ['agent', 'adopted', true, false, true]);
assert.deepEqual(s.roots.map(r => r.name), ['guild', 'ada']);
assert.equal(s.nodes.get(bo.id).badges[0].by, ada.id);
});
test('in the lab a seat goes independent: its own root and keys, its money along, and acts of its own', () => {
const lab = emptyLab();
const root = act(lab, 'found', null, { name: 'guild' }), ada = act(lab, 'birth', root, { name: 'ada', kind: 'agent' });
send(lab, root, ada, SAT);
const before = payerOf(lab.keys, lineageState(lab), lineageState(lab).nodes.get(ada)).address;
const freed = act(lab, 'graduate', root, { of: ada });
const s = lineageState(lab), node = s.nodes.get(ada), k = keyOf(lab.keys, s, node);
assert.deepEqual([k.account, k.from, holderOf(lab, s, ada).who, node.rooted], [0, 'ada root', 'own', freed]);
assert.equal(secretsOf(lab.keys, s, node).seed.mnemonic, lab.keys.adopted[freed]);
const purse = payerOf(lab.keys, s, node);
assert.notEqual(purse.address, before);
assert.ok(lab.chain.balance(purse.script) > 0.99 * SAT, 'its money moved to its new SegWit address');
setTempo(lab, 'each');
const studio = act(lab, 'birth', ada, { name: 'studio' });
act(lab, 'charter', ada, { form: 'llc', jurisdiction: 'US-WY', legal: 'Ada LLC', provider: 'doola' });
act(lab, 'charter', ada, { form: 'series-llc', jurisdiction: 'US-WY', legal: 'Ada LLC' });
act(lab, 'charter', ada, { form: 'series', of: studio });
const after = lineageState(lab);
assert.deepEqual([after.nodes.get(ada).charter.form, after.nodes.get(studio).charter.form, standing(after, after.nodes.get(studio)).ok],
['series-llc', 'series', true]);
assert.throws(() => act(lab, 'charter', studio, { form: 'sole' }), /A sole proprietor is a person/);
});