2026-10-15 15:48:31 -07:00
import test from 'node:test' ;
import assert from 'node:assert/strict' ;
import { MetadataIndex } from '../src/adapters/metadata-index.js' ;
import { holderCoin } from '../src/adapters/handoff.js' ;
import { offsetOf } from '../src/adapters/sats.js' ;
import { emptyLab , setRule } from '../src/orgchain/store.js' ;
import { act , deposit , gasCost , lineageState , offer , sell , send , take } from '../src/orgchain/actions.js' ;
import { SAT , tankOf } from '../src/orgchain/money.js' ;
import { keyOf , scriptHex } from '../src/orgchain/keys.js' ;
import { askId } from '../src/orgchain/offers.js' ;
import { BELT_GAS , charmsOf } from '../src/orgchain/belts.js' ;
import { PROTOCOL } from '../src/orgchain/lineage.js' ;
2026-10-15 16:10:18 -07:00
import { InscriptionAdapter } from '../src/adapters/inscription.js' ;
2026-10-15 15:48:31 -07:00
import { Script , Tap , Tx } from '@cmdcode/tapscript' ;
import { FEE , envelope , tapleafOf } from '../src/adapters/inscription.js' ;
import { getVin , getVout } from '../src/upstream/primitives.js' ;
// Belts are soulbound (v1.14): the reducer binds a belt to the node it names, and the lab inscribes it on that node's own
// sat, so it goes wherever the node goes and never sells apart from it.
function twoHoldings () {
const lab = emptyLab ();
setRule ( lab , 'none' );
const acme = act ( lab , 'found' , null , { name : 'acme' }), beta = act ( lab , 'found' , null , { name : 'beta' });
deposit ( lab , acme , 10 * SAT );
deposit ( lab , beta , 10 * SAT );
return { lab , acme , beta };
}
// Where an inscription sits: its outpoint and its sat's offset in it, as ord writes a satpoint.
const satpoint = ( lab , id ) => { const c = holderCoin ( lab . chain , id ); return ` ${ c . txid } : ${ c . vout } : ${ offsetOf ( c . ranges , c . inscriptions . find ( i => i . id === id ). sat ) } ` ; };
const charms = ( lab , id ) => { const index = new MetadataIndex ( lab . chain ); return charmsOf ( lab . chain , index , index . events . find ( e => e . id === id )); };
const tank = ( lab , id ) => { const s = lineageState ( lab ); return tankOf ( lab . chain , keyOf ( lab . keys , s , s . nodes . get ( id )). wallet ); };
test ( "a belt rides its recipient's own sat: the issuer authorizes it and pays two fees, the recipient co-signs, ord marks it ♻️" , () => {
const { lab , acme , beta } = twoHoldings (), ops = act ( lab , 'birth' , acme , { name : 'ops' });
send ( lab , acme , ops , SAT );
const bot = act ( lab , 'birth' , ops , { name : 'bot' , kind : 'agent' }), had = [ tank ( lab , ops ), tank ( lab , bot )];
const white = act ( lab , 'award' , ops , { to : bot , skill : 'launch' , belt : 'white' });
assert . deepEqual ([ gasCost ( 'award' ), had [ 0 ] - tank ( lab , ops ), tank ( lab , bot ) - had [ 1 ]], [ 1000 , 1000 , 0 ]); // no postage: it rides bot's sat
assert . equal ( satpoint ( lab , white ), satpoint ( lab , bot ));
assert . deepEqual ([ charms ( lab , bot ), charms ( lab , white )], [[ 'vindicated' ], [ 'reinscription' , 'vindicated' ]]);
// A newer belt in the skill replaces it on the record; the sat keeps both, in order: its history travels with it.
const green = act ( lab , 'award' , ops , { to : bot , skill : 'launch' , belt : 'green' });
assert . deepEqual ( lineageState ( lab ). nodes . get ( bot ). badges . map ( b => [ b . skill , b . belt ]), [[ 'launch' , 'green' ]]);
assert . deepEqual ( holderCoin ( lab . chain , bot ). inscriptions . map ( i => i . id ), [ bot , white , green ]);
// A registry in another holding awards it too, and bot co-signs again.
const registry = act ( lab , 'birth' , beta , { name : 'registry' });
send ( lab , beta , registry , SAT );
const listen = act ( lab , 'award' , registry , { to : bot , skill : 'listen' , belt : 'white' });
assert . deepEqual ([ satpoint ( lab , listen ), lineageState ( lab ). log . at ( - 1 ). roots . length ], [ satpoint ( lab , bot ), 2 ]);
// Bot acts as itself, and its belts stay on its sat: an act spends its coin and gives it back whole.
send ( lab , ops , bot , SAT / 10 );
act ( lab , 'decide' , bot , { title : 'Ship it' });
assert . deepEqual ( holderCoin ( lab . chain , bot ). inscriptions . map ( i => i . id ), [ bot , white , green , listen ]);
});
test ( 'a belt never sells apart from its agent: it is no lot, it can not be awarded into a tree changing hands, and it travels with its tree' , () => {
const { lab , acme , beta } = twoHoldings (), ops = act ( lab , 'birth' , acme , { name : 'ops' });
send ( lab , acme , ops , SAT );
const bot = act ( lab , 'birth' , ops , { name : 'bot' , kind : 'agent' }), belt = act ( lab , 'award' , ops , { to : bot , skill : 'launch' , belt : 'green' });
assert . throws (() => sell ( lab , belt , SAT ), /^Error: Pick an item, an org or a holding to sell\.$/ );
assert . throws (() => offer ( lab , belt , beta , SAT ), /^Error: Pick an item, an org or a holding to make an offer on\.$/ );
const a = sell ( lab , ops , 2 * SAT );
assert . deepEqual ( lab . chain . utxos . get ( askId ( a )). inscriptions . map ( i => i . id ), [ ops , bot , belt ]);
assert . throws (() => act ( lab , 'award' , acme , { to : bot , skill : 'post' , belt : 'white' }), /^Error: ops is listed for sale at 2 tBTC\. Withdraw its ask first\.$/ );
take ( lab , askId ( a ), beta );
// After the split, the belt is still on bot's sat, which is at bot's new key, under beta.
const s = lineageState ( lab ), node = s . nodes . get ( bot );
assert . equal ( satpoint ( lab , belt ), satpoint ( lab , bot ));
assert . equal ( scriptHex ( holderCoin ( lab . chain , bot ). scriptPubKey ), scriptHex ( keyOf ( lab . keys , s , node ). wallet . script ));
assert . deepEqual ([ node . root , node . badges . map ( b => [ b . skill , b . belt , s . nodes . get ( b . by ). name ])], [ beta , [[ 'launch' , 'green' , 'ops' ]]]);
});
// An award's two transactions by hand, with one thing changed, to try the chain's own rule for a reveal on a held sat.
function forge ( lab , by , to , { pointer , parent = by , vout } = {}) {
const s = lineageState ( lab ), wallet = id => keyOf ( lab . keys , s , s . nodes . get ( id )). wallet , inscriber = wallet ( by );
const held = holderCoin ( lab . chain , to ), own = holderCoin ( lab . chain , by ), coin = c => getVin ( c . txid , c . vout , c . value , c . scriptPubKey );
const pay = lab . chain . coins ( inscriber . script ). find ( c => ! c . inscriptions ? . length && c . value >= BELT_GAS + 330 );
const script = envelope ( inscriber . pubkey , 'award launch white' , parent , { op : 'award' , to , skill : 'launch' , belt : 'white' },
{ pointer : pointer ?? offsetOf ( held . ranges , held . inscriptions . find ( i => i . id === to ). sat ), metaprotocol : PROTOCOL });
const leaf = tapleafOf ( script ), [ tapkey , control ] = Tap . getPubKey ( inscriber . pubkey , { target : leaf });
const commit = Tx . create ({ vin : [ coin ( pay )], vout : [ getVout ( FEE , [ 'OP_1' , tapkey ]), getVout ( pay . value - BELT_GAS , inscriber . script )] });
commit . vin [ 0 ]. witness = [ inscriber . sign ( commit )];
const reveal = Tx . create ({ vin : [ coin ( held ), coin ( own ), getVin ( Tx . util . getTxid ( commit ), 0 , FEE , [ 'OP_1' , tapkey ])],
vout : vout ? .( held , own , inscriber ) ?? [ getVout ( held . value , held . scriptPubKey ), getVout ( own . value , own . scriptPubKey )] });
reveal . vin [ 2 ]. witness = [ inscriber . sign ( reveal , leaf , 2 ), Script . encode ( script , false ). hex , control ];
reveal . vin [ 1 ]. witness = [ inscriber . sign ( reveal , undefined , 1 )];
reveal . vin [ 0 ]. witness = [ wallet ( to ). sign ( reveal , undefined , 0 )];
return () => lab . chain . submitPackage ([ Tx . encode ( commit ). hex , Tx . encode ( reveal ). hex ]);
}
test ( 'the chain takes a three-input reveal only to inscribe on a held sat: its parent spent, both coins back unchanged, the pointer on an inscribed sat' , () => {
const { lab , acme } = twoHoldings (), ops = act ( lab , 'birth' , acme , { name : 'ops' });
send ( lab , acme , ops , SAT );
const bot = act ( lab , 'birth' , ops , { name : 'bot' , kind : 'agent' }), coin = holderCoin ( lab . chain , bot );
const at = offsetOf ( coin . ranges , coin . inscriptions [ 0 ]. sat ), mempool = lab . chain . mempool . length ;
assert . throws ( forge ( lab , ops , bot , { pointer : at + 1 }), /^Error: An inscription on a held sat must point at an inscribed sat\.$/ );
assert . throws ( forge ( lab , ops , bot , { pointer : coin . value }), /^Error: An inscription on a held sat must point at an inscribed sat\.$/ );
assert . throws ( forge ( lab , ops , bot , { parent : acme }), /^Error: An inscription on a held sat needs its parent spent, from the inscriber's key\.$/ );
assert . throws ( forge ( lab , ops , bot , { vout : ( held , own , me ) => [ getVout ( held . value , me . script ), getVout ( own . value , own . scriptPubKey )] }),
/^Error: An inscription on a held sat returns both coins unchanged\.$/ );
assert . throws ( forge ( lab , ops , bot , { vout : ( held , own ) => [ getVout ( held . value - 100 , held . scriptPubKey ), getVout ( own . value + 100 , own . scriptPubKey )] }),
/^Error: An inscription on a held sat returns both coins unchanged\.$/ );
assert . equal ( lab . chain . mempool . length , mempool ); // each rejected package left nothing behind, its commit included
forge ( lab , ops , bot )();
const belt = holderCoin ( lab . chain , bot ). inscriptions . at ( - 1 ). id ;
assert . deepEqual ([ holderCoin ( lab . chain , bot ). inscriptions . length , satpoint ( lab , belt )], [ 2 , satpoint ( lab , bot )]);
assert . deepEqual ( lineageState ( lab ). nodes . get ( bot ). badges . map ( b => [ b . skill , b . belt ]), [[ 'launch' , 'white' ]]);
});
2026-10-15 16:10:18 -07:00
test ( 'a belt inscribed the old way, with postage on a sat of its own, does not count' , () => {
const { lab , acme } = twoHoldings (), ops = act ( lab , 'birth' , acme , { name : 'ops' });
send ( lab , acme , ops , SAT );
const bot = act ( lab , 'birth' , ops , { name : 'bot' , kind : 'agent' }), s = lineageState ( lab );
const id = new InscriptionAdapter ( lab . chain , keyOf ( lab . keys , s , s . nodes . get ( ops )). wallet )
. inscribe ( 'award launch white' , ops , { op : 'award' , to : bot , skill : 'launch' , belt : 'white' }, { metaprotocol : PROTOCOL });
const after = lineageState ( lab ), entry = after . log . find ( e => e . id === id );
assert . deepEqual ([ entry . ok , entry . why , after . nodes . get ( bot ). badges ], [ false , "a belt counts only on its recipient's own sat" , []]);
assert . notEqual ( satpoint ( lab , id ), satpoint ( lab , bot ));
});