From adca2709f09e571bfbadc34232eac2e1110eb629 Mon Sep 17 00:00:00 2001 From: Hyperbox Date: Thu, 15 Oct 2026 13:32:29 -0700 Subject: [PATCH] Orgchain v1.12: both sides of a deal, and orgs change hands Deals go both ways now, OpenOrdex's partially signed transaction from either side, on a lot: an item, or an org inside a holding, which its parent sells. Buy makes an offer, the buy order: the buyer signs the whole deal and the holder countersigns. Sell makes an ask, the sell order: the lot's key signs its input alone with SIGHASH_SINGLE|ANYONECANPAY, committing to that input and to the price to the seller and to nothing else, and any node can take it, leading with a 600-sat coin of its own so the lot lands in its output. An org sells cash-free: before its parent lists it or accepts an offer on it, the org's money, gas tank and items go home, so the price is for the org alone, and a listed org is frozen until its ask is taken or withdrawn. Its buyer adopts it with two new lineage/v0 ops, in either order: join, signed by whoever holds the org's inscription, and adopt, signed by the new parent. The org takes the buyer's next index and derives its keys there, and its inscription moves to its new key. Taking an ask adopts at once; after an accepted offer the buyer adopts when it's ready. The seller's tree keeps a row at the old index. Rehearsed on Bitcoin Core regtest: an org sold on an ask, another on an offer and an item on an ask, twelve beats and 104 transactions, and ord tells the same story. The rehearsal caught that a miner can put a join and its adoption in either order within a block, so a move records both halves and each half belongs to both holdings. The sample ends with both sides standing: lineage's offer for the launch kit, and well-known's ask for mailr. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01H4upF5LVwmPDDqrhdUfnXh --- AGENTS.md | 12 +- README.md | 54 +++--- SPEC.md | 71 +++++--- bin/lineage-index.mjs | 5 +- index.html | 26 ++- src/main.js | 190 ++++++++++++++++----- src/orgchain.css | 3 + src/orgchain/actions.js | 118 +++++++++++-- src/orgchain/keys.js | 6 +- src/orgchain/lineage.js | 100 +++++++++--- src/orgchain/money.js | 47 +++++- src/orgchain/offers.js | 227 ++++++++++++++++++++++---- src/orgchain/store.js | 17 +- tests/acquire.test.js | 178 ++++++++++++++++++++ tests/fixtures/regtest-rehearsal.json | 2 +- tests/indexer.test.js | 21 ++- tests/offers.test.js | 15 +- tests/orgchain.spec.js | 86 +++++++++- tools/regtest/README.md | 14 +- tools/regtest/rehearse.mjs | 21 ++- 20 files changed, 1018 insertions(+), 195 deletions(-) create mode 100644 tests/acquire.test.js diff --git a/AGENTS.md b/AGENTS.md index b059f84..d4b3435 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,9 +18,15 @@ Orgchain is lineage's app for creating and operating lineage holdings on Super T as change, and balances are read off the chain, never stored. Gas (`src/orgchain/actions.js`): every act is paid by the actor's own keys and checked before anything is signed; the lab wallet pays only for foundings and for holdings that predate lab keys. -- Deals live in `src/orgchain/offers.js`: the buyer pre-signs the whole transaction and the holder countersigns. - An offer is signatures kept in the lab, never chain state, and who holds an item is read off the chain, never - from metadata. Inscription metadata is public and permanent, so never put a secret in it (item notes included), and a +- Deals live in `src/orgchain/offers.js`, both sides of the PSBT: an offer (the buyer pre-signs the whole transaction + and the holder countersigns) and an ask (the holder signs the lot's input alone, SIGHASH_SINGLE|ANYONECANPAY, and any + buyer completes it behind a dummy of its own). Offers and asks are signatures kept in the lab, never chain state, and + who holds a lot is read off the chain, never from metadata. An org sells cash-free and is frozen while listed or sold + (`actions.js` cashOut, frozenWhy), and its buyer's keys sign the adoption: never sign for the buyer at the seller's + act. +- Moves (`join`, `adopt`): neither half's effect may depend on their order within a block, since a miner may put them + either way; the move records both halves. An old parent keeps a moved child in `children` so its index is never + reused: walk children with `kidsOf`. Inscription metadata is public and permanent, so never put a secret in it (item notes included), and a charter carries a hash of the filings, never the filings. - Charters (`src/orgchain/lineage.js`): legal form is an attribute of a node, and whether a charter stands is derived from the tree (`standing`), never stored, so charters commute with graduations and closes. diff --git a/README.md b/README.md index 96de02b..67faeb7 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,12 @@ chain itself records who did what, and when. - **Found** a holding (a root: the LLC). **Birth** orgs and agents under any org. **Award** a belt (one skill, one seat, in any holding: a registry like well-known certifies agents it doesn't hold). **Decide** (a decision on a node's - record). **Make** an item (an inscription its maker holds, and can sell). **Keyring** (a node publishes its keys on - each chain, each key signing the same statement). **Charter** (a node's legal form, from sole proprietor to series - LLC). **Graduate** an org or an agent: its edge flips from derived to adopted, and it becomes a holding of its own, - with its own tree, timeline and share of the mining, while its parent's tree keeps a row for it. **Close** a node - (derived children close with it; graduated ones hold their own roots and stay open). + record). **Make** an item (an inscription its maker holds, and can sell). **Buy** an item or an org another node + holds, and **Sell** your own (both sides of a deal, below). **Keyring** (a node publishes its keys on each chain, each + key signing the same statement). **Charter** (a node's legal form, from sole proprietor to series LLC). **Graduate** + an org or an agent: its edge flips from derived to adopted, and it becomes a holding of its own, with its own tree, + timeline and share of the mining, while its parent's tree keeps a row for it. **Close** a node (derived children close + with it; graduated ones hold their own roots and stay open). - **Time.** A beat is a block. At the default tempo each act is its own beat; at 10, 30 or 60 seconds, acts are cued and land together on the next beat; on manual they wait for *Beat now*. The slider and *Replay* show a holding as it stood at any beat. @@ -33,13 +34,24 @@ chain itself records who did what, and when. up from its own money 0.0001 test BTC at a time. A node with neither can't act until someone sends it money, and a graduating org pays for the claim on its new root itself. The lab wallet pays only for foundings (a holding owns nothing until it mines) and for holdings that predate lab keys. -- **Deals.** Any active node can **Offer** on an item another node holds, in any holding: OpenOrdex's buy order, with - a price. Its SegWit key signs the whole deal at once (SIGHASH_ALL): the item and its own coins in; the item to its - Taproot key, the price to the holder's SegWit address and its change out. Nothing reaches the chain until the - holder **Accepts** by countersigning the item's input, so the offer stands, kept in the lab like a PSBT in an - order book, until it is accepted, withdrawn, or goes stale (a coin it promised moved, or a side closed or re-keyed). - The buyer pays the fee; a price of 0 is a free claim. Payments spend coins no offer has promised first. Accepted - offers stay as the deals' receipts, and the timeline and Inspect show each deal (`src/orgchain/offers.js`). +- **Deals.** Both sides of OpenOrdex's partially signed transaction, on a lot: an item, or an org inside a holding, + which its parent sells. **Buy** makes an offer, the buy order: the buyer's SegWit key signs the whole deal at once + (SIGHASH_ALL), the lot and its own coins in, the lot to its Taproot key, the price to the seller's SegWit address and + its change out, and nothing reaches the chain until the holder **Accepts** by countersigning the lot's input. **Sell** + makes an ask, the sell order: the lot's key signs its input alone with SIGHASH_SINGLE|ANYONECANPAY (0x83), which + commits to that input and to the price to the seller at the same index and to nothing else, so any node can **Take** + it. The buyer leads with a 600-sat coin of its own, so the lot lands in its first output (sats move first in, first + out), then the lot and its funds; out go the lot to the buyer and the price to the seller. Offers and asks stand in + the lab like PSBTs in an order book until they land, are withdrawn, or go stale (a coin they spend moved, or a side + closed or re-keyed). The buyer pays the fees, and an offer of 0 is a free claim. Payments spend coins no offer has + promised first, and the timeline and Inspect show each deal (`src/orgchain/offers.js`). +- **Acquisitions.** An org sells cash-free: before its parent lists it or accepts an offer on it, the org's money, gas + tank and items go home to the parent, so the price is for the org alone, and a listed org is frozen until its ask is + taken or withdrawn. Its buyer then adopts it: a `join`, signed by the key that now holds the org's inscription, and an + `adopt`, signed by the buyer, move it into the buyer's tree at the buyer's next index, where its keys derive from the + buyer's seed, and its inscription moves to its new key. Taking an ask adopts at once; after an accepted offer the + buyer adopts when it's ready. The seller's tree keeps a row at the old index, marked with where the org went. The + sample ends with both sides standing: lineage's offer for the launch kit, and well-known's ask for mailr. - **Keyrings.** **Keyring** publishes a node's keys in one inscription whose parent is the node itself: its Taproot and SegWit addresses, its npub and its Ethereum address, so another chain or a registry like well-known can find them without deriving anything. Every key signs the same statement, the way its own wallets sign a message: BIP-322 for the @@ -96,11 +108,13 @@ keyring's is the statement its keys signed). | `charter` | the node itself, or a series' master; the newest counts | `form`; `jurisdiction`, `legal`, `provider`, `filing`, or `of` for a series | | `graduate` | the child's parent (then the handoff to the child's fresh root); the child, org or agent, becomes a holding | `of` | | `close` | the node itself | none | +| `join` | the org, signed by whoever holds its inscription (its buyer); with the `adopt`, in either order, it moves | `to` | +| `adopt` | the new parent, which takes the org in at its next index; its keys derive from there | `of` | Names are domain labels (lowercase letters, digits, hyphens). Org and agent indices count separately and are never -reused. The metadata says what an event means; the parent it spends proves who was allowed to make it; who holds an -item afterwards is the chain's to say, never the metadata's. Metadata is public and permanent: an item's note is for a -hash or a link, never a secret or a document. +reused (an org that changes hands takes its buyer's next one). The metadata says what an event means; the parent it +spends proves who was allowed to make it; who holds an item afterwards is the chain's to say, never the metadata's. +Metadata is public and permanent: an item's note is for a hash or a link, never a secret or a document. ## Lineage as a metaprotocol @@ -112,10 +126,12 @@ fold in, why a parent proves authority, and every rule the reducer applies. The node bin/lineage-index.mjs --ord http://127.0.0.1:8088 [--from ] [--compare ] ``` -`tools/regtest/` rehearses the sample, keyrings included, on Bitcoin Core regtest: the lab's own transactions, byte for -byte, confirmed by Bitcoin Core and indexed by `ord`, which then tell the same story as the lab (`tests/indexer.test.js` -replays a recorded run). The first rehearsal found that the lab's reveals committed to the wrong tapleaf, which real -Bitcoin rejects; reveals are now checked the way Bitcoin checks them (`SPEC.md`, section 1). +`tools/regtest/` rehearses the sample, keyrings, asks, offers and moves included, on Bitcoin Core regtest: the lab's own +transactions, byte for byte, confirmed by Bitcoin Core and indexed by `ord`, which then tell the same story as the lab +(`tests/indexer.test.js` replays a recorded run). The first rehearsal found that the lab's reveals committed to the +wrong tapleaf, which real Bitcoin rejects; reveals are now checked the way Bitcoin checks them (`SPEC.md`, section 1). +The rehearsal of org sales found that a miner can put a join and its adoption in either order within a block, so a move +records both halves and each half belongs to both holdings (`SPEC.md`, section 5). ## Build and test diff --git a/SPEC.md b/SPEC.md index 47986bd..5262ecf 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,9 +1,9 @@ # lineage/v0 lineage/v0 is an ordinals metaprotocol for lineage holdings: LLCs, the orgs and agents they birth, the belts and -decisions on their record, and the items they make. Every event is an inscription. The chain orders the events and -proves who was allowed to make each one; the state (the trees) is stored nowhere, and every indexer that applies these -rules to the same inscriptions gets the same trees. +decisions on their record, the items they make, and the orgs that change hands between them. Every event is an +inscription. The chain orders the events and proves who was allowed to make each one; the state (the trees) is stored +nowhere, and every indexer that applies these rules to the same inscriptions gets the same trees. Status: test networks only (Orgchain's in-browser chain and Bitcoin Core regtest). The reference implementation is `reduce()` in `src/orgchain/lineage.js`; `src/indexer/ord.js` and `bin/lineage-index.mjs` run it over a real ord index. @@ -41,7 +41,8 @@ in section 6 that changes nothing. A node is created by an accepted `found` (a holding) or `birth` (an org or an agent), and its id is that event's inscription id. Each node has a kind (holding, org or agent), a status (active or closed), and, below a holding, an edge: derived (the parent holds the child's seed) or adopted (it graduated and holds its own root). A child's index -counts its parent's earlier children of the same kind, orgs and agents separately, and is never reused. +counts its parent's earlier children of the same kind, orgs and agents separately, and is never reused: an org that +changes hands (section 5) takes the next index under its new parent, and its old parent keeps the old one, unused. An event's parent is the node that authorizes it, and it counts only if the inscribing transaction spends that node's inscription: ord records a parent only then ([provenance](https://docs.ordinals.com/inscriptions/provenance.html)). @@ -61,13 +62,15 @@ the inscription moves the authority. | `charter` | the node itself, active; for a series, its master | `form`; `jurisdiction`, `legal`, `provider` and `filing`, or `of` for a series | the node's legal form, replacing its earlier charter (section 5) | | `graduate` | the child's parent, active | `of` (an active child, org or agent, not yet adopted) | the edge becomes adopted, and the child becomes a holding of its own (section 5) | | `close` | the node itself, active | none | the node closes, with every active derived descendant; adopted ones stay open | +| `join` | the org itself, active | `to` (the new parent) | half of a move: the org asks to join `to`, and moves once `to` adopts it (section 5) | +| `adopt` | the new parent, active | `of` (the org) | the other half: `to` takes the org in, and it moves once it has joined (section 5) | Names (holdings, children, skills, items) are 1 to 40 lowercase letters, digits or hyphens, starting and ending with a letter or digit, because they become domains. Text fields are trimmed, and only their first characters count: 140 for a title, 400 for a why, 280 for a note, 120 for a legal name. Agents birth and award only once they graduate. An event that breaks a rule is rejected: it stays on the chain and changes nothing. Any other `op` is rejected. -## 5. Holdings, graduation and charters +## 5. Holdings, graduation, moves and charters A holding is a root: a `found`, or a child that graduated. When an org graduates, it and its descendants still in the old holding re-root under it, and it joins the holdings. An agent can graduate too: it goes independent, and as a holding @@ -78,6 +81,21 @@ root, and so do its derived descendants', so their keyrings retire (section 8). In Orgchain a graduation is followed by a handoff: the graduate's fresh root claims its inscription, and the derived key that held it countersigns. After that, only the new root can act for it. +An org can change hands: move to a new parent, in its own holding or in another. Both sides must authorize it, and an +event has one parent (section 1), so a move takes two events: a `join`, whose parent is the org (so whoever holds the +org's inscription, its buyer after a sale), names the new parent in `to`, and an `adopt`, whose parent is the new +parent, names the org in `of`. Either may come first, and the second completes the move. A newer `join` replaces the +org's pending one; an `adopt` waits for its own join. Only an org inside a holding moves (a holding's root would take +its whole tree, which is another matter), and it moves alone: an org with active derived children does not. The new +parent is an active holding, org or graduated agent, not the org nor anything below it, with no active child of the +org's name. + +When the move completes, the org takes the next index of its kind under its new parent, and it and its derived +descendants still in its old holding re-root into the new one. Its keys now derive from its new parent at its new index, +so its keyring retires (section 8). Its charter goes with it; a series that leaves its master's holding lapses. The move +records both halves, and each half belongs to both holdings, so the miner may put them in either order within a block. A +graduation of an org with a pending join is rejected. + A `charter` records a node's legal form: what it is off chain. Legal form is an attribute, not an identity, so a node keeps its inscription, history and keys whatever form it takes, and the newest charter counts. @@ -103,17 +121,29 @@ same block land the same way in either order. Control follows the inscription, w ## 6. Order-sensitive conflicts Where independent events conflict within one block, the block's order decides. These are the cases: a close and an act -by one of its derived descendants; an award and its recipient's close; two foundings with the same name; a keyring and -a graduation or close that retires it; two keyrings that list the same key. Orgchain keeps a keyring and the graduation -that would retire it a block apart. +by one of its derived descendants; an award and its recipient's close; two foundings with the same name; a keyring and a +graduation or close that retires it; two keyrings that list the same key; a join or adoption and a graduation, close or +birth that changes the org or its new parent. Orgchain keeps a keyring and the graduation that would retire it a block +apart, and it freezes an org that is listed or sold, so its seller can't graduate it before its buyer adopts it. ## 7. Ownership, items and deals Whoever controls the output that holds an inscription's sat holds that node or item; for indexers that is ord's -satpoint. Items move like any inscription. A deal is not an event: it is one transaction that carries the item and the -buyer's coins in, and the item to the buyer, the price to the holder and the buyer's change out. The buyer signs its -inputs first (SIGHASH_ALL) and the holder countersigns the item's. ord sees the item move, and nothing else needs -indexing. +satpoint. Items and orgs move like any inscription. A deal is not an event: ord sees the lot move, and nothing else +needs indexing. Orgchain makes deals both ways OpenOrdex does, on a lot that is an item or an org: + +- An offer is a buy order: one transaction with the lot and the buyer's coins in, and the lot to the buyer, the price + to the seller and the buyer's change out. The buyer signs its inputs first (SIGHASH_ALL), and the lot's holder + countersigns. +- An ask is a sell order: the holder signs the lot's input alone with SIGHASH_SINGLE|ANYONECANPAY (0x83), which commits + to that input and to the output at its index, the price to the seller, and to nothing else. Any buyer completes it: + a small coin of its own, the lot and its funds in; the lot to the buyer's key and the price to the seller out. The + small coin leads so that the lot's sat lands in the buyer's output, since sats move first in, first out. + +An org's seller is its parent, and a sold org joins its buyer's tree by `join` and `adopt` (section 5); until then the +trees still draw it under its seller. Orgchain sells an org cash-free: before it lists one or accepts an offer on one, +the parent takes home the org's money, gas tank and items, so the price is for the org alone, and a listed org is frozen +until its ask is taken or withdrawn. ## 8. Keys and keyrings @@ -150,11 +180,12 @@ any wallet can make a proof and any library can check one. A Taproot proof is a 65-byte one ending in SIGHASH_ALL; a P2WPKH proof is a low-S DER signature with SIGHASH_ALL and the compressed key. The inscription's body is the statement, for people to read; the rules rebuild it from the metadata. -A keyring is rejected unless every key is well formed and every proof checks out. While a node's keyring stands, its keys -are bound to that node, and a keyring that lists a key bound to another node is rejected; a key binds once, whatever -network it is written for. A node's newer keyring replaces its older one and frees the keys it no longer lists. A -graduation retires the keyrings of the graduate and its derived descendants, since their keys change with the new root, -and a close retires the keyrings of the nodes it closes. A retired keyring binds nothing, so the node publishes a new one. +A keyring is rejected unless every key is well formed and every proof checks out. While a node's keyring stands, its +keys are bound to that node, and a keyring that lists a key bound to another node is rejected; a key binds once, +whatever network it is written for. A node's newer keyring replaces its older one and frees the keys it no longer lists. +A graduation retires the keyrings of the graduate and its derived descendants, since their keys change with the new +root, a move retires the moving org's, since its keys change with its parent, and a close retires the keyrings of the +nodes it closes. A retired keyring binds nothing, so the node publishes a new one. Lineage's key tree page also makes an agent's keyring as a nostr event (kind 30078, `d` = `lineage/keyring/v0`), signed by its npub and listing its leaves too. The inscription is the chain's record, with a proof from every key. @@ -169,8 +200,8 @@ chain's own wallets sign a message. ## 10. Conformance -`tools/regtest` rehearses Orgchain's sample on Bitcoin Core regtest, byte for byte, keyrings included, and -`bin/lineage-index.mjs --compare` checks that ord and the lab tell the same story: the same events in the same beats, -the same metadata and parents, the same trees and keyrings, and every inscription in the same output. +`tools/regtest` rehearses Orgchain's sample on Bitcoin Core regtest, byte for byte, keyrings, asks, offers and moves +included, and `bin/lineage-index.mjs --compare` checks that ord and the lab tell the same story: the same events in the +same beats, the same metadata and parents, the same trees and keyrings, and every inscription in the same output. `tests/indexer.test.js` replays a recorded run. `tests/keyring.test.js` checks the proofs against BIP-322's published vectors, the key tree page's Ethereum vector, and values cross-checked with bip322-js, ethers and nostr-tools. diff --git a/bin/lineage-index.mjs b/bin/lineage-index.mjs index 29f303f..fb0b9ca 100644 --- a/bin/lineage-index.mjs +++ b/bin/lineage-index.mjs @@ -16,8 +16,11 @@ 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')); + console.log(rows(state, root.id).map(r => (r.type === 'group' ? `${r.guide}${r.label}` + : `${r.guide}${r.path} ${r.node.name}${r.moved ? ` · moved to ${state.nodes.get(r.node.parent).name}` : r.away ? ' · graduated' : ''}`)).join('\n')); } +const moves = [...state.nodes.values()].flatMap(n => n.moves.map(m => `${n.name} from ${state.nodes.get(m.from).name} to ${state.nodes.get(m.to).name}`)); +if (moves.length) console.log(`\n${plural(moves.length, 'org')} changed hands: ${moves.join('; ')}`); 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})`); diff --git a/index.html b/index.html index 60d3711..de7dfa0 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ Orgchain · Lineage - + @@ -16,7 +16,7 @@

Lineage /

Orgchain

-

Found a holding, then birth its orgs and agents, award belts, record decisions, make and trade items, publish keyrings, charter legal forms, send test BTC, graduate and close. Each act is an inscription whose parent is the node that authorizes it, paid for with the actor's own gas, and each beat is a block whose 50 test BTC the holdings mine. Lineage gives the structure; the chain adds time.

+

Found a holding, then birth its orgs and agents, award belts, record decisions, make items, buy and sell items and orgs, publish keyrings, charter legal forms, send test BTC, graduate and close. Each act is an inscription whose parent is the node that authorizes it, paid for with the actor's own gas, and each beat is a block whose 50 test BTC the holdings mine. Lineage gives the structure; the chain adds time.

Test coins only @@ -88,7 +88,7 @@

Select a holding, or a node in its tree, to act through it.