Orgchain v1.14: soulbound belts ride their seats' sats
A belt is soulbound: the reducer already bound it to the seat it names, and now the lab inscribes it on that seat's own sat, so by ordinal theory it goes wherever the seat goes and never sells apart from it. One reveal carries an award, three coins in: the seat's, which the pointer puts the belt in and the seat's own key signs, accepting it; the issuer's, the parent; and the commit, which pays the fee. Both coins come back unchanged, so an award needs no postage and costs two fees (1,000 sats). ord marks it a reinscription and vindicated, since a pointer and an envelope past the first input would have cursed it before the jubilee. The engine takes that three-input reveal and nothing looser: the parent spent from the inscriber's key, both coins returned unchanged, the pointer on an inscribed sat of the first. A belt is never a lot, and an award can't land in a tree that is listed or sold. Trees count and split by inscribed sat, so a seat's belts stay on it through every sale. Inspect shows a node's belts and ord's charms, and the award form says whose sat the belt will ride. Belts from before v1.14 stay with their issuers and count the same. Rehearsed on Bitcoin Core regtest from height 161: sixteen beats and 131 transactions; ord charms the three belts as the lab does, and after two whole-tree sales each belt shares its seat's satpoint. The compare now checks satpoints and charms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4upF5LVwmPDDqrhdUfnXh
This commit is contained in:
1 parent
74ed801176
commit
c8c18b073e
18 files changed
+368
-107
No files matched your search
@@ -0,0 +1,107 @@
|
||||
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';
|
||||
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']]);
|
||||
});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"from":144,"ord":{"/blockheight":{"status":200,"body":"160"},"/inscriptions/block/144/0":{"status":200,"body":"{\"ids\":[],\"more\":false,\"page_index\":0}"},"/inscriptions/block/145/0":{"status":200,"body":"{\"ids\":[\"4a05a94d9148640bddbd7c0f429f889c9ae4a97c15eaeda43cf382f1ad8248d2i0\"],\"more\":false,\"page_index\":0}"},"/inscription/4a05a94d9148640bddbd7c0f429f889c9ae4a97c15eaeda43cf382f1ad8248d2i0":{"status":200,"body":"{\"address\":\"bcrt1p3ejmxr9fvv4pw03me8tmpgf50aw8sx67v3hsr5paw9v9qsnnjq5s3p8emj\",\"charms\":[],\"child_count\":4,\"children\":[\"f57fcba7f56588525dc5ac371b5fd2c8a5fdfd6b7b1be0c61e708abc543d79f6i0\",\"57b8455f43a8c008b1fc409d4476bdc9436a50d04ff216056a573d279dd9ced0i0\",\"71fd1fee2878d54817ab4b10bd702cf6f2184132010a94cc90aa33ff402e3a2ei0\",\"06f96791d95d05570008cc89ef0da57799e7576cdb78d4d985a0cff788a171d9i0\"],\"content_length\":17,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":145,\"id\":\"4a05a94d9148640bddbd7c0f429f889c9ae4a97c15eaeda43cf382f1ad8248d2i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"f57fcba7f56588525dc5ac371b5fd2c8a5fdfd6b7b1be0c61e708abc543d79f6i0\",\"number\":56,\"parents\":[],\"previous\":\"4c21347dec20910af995c2cfc1c68ae4c333f2b62041a76df75ad4c8df0e851ai0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"06f96791d95d05570008cc89ef0da57799e7576cdb78d4d985a0cff788a171d9:0:0\",\"timestamp\":1792100539,\"value\":1000}"},"/r/metadata/4a05a94d9148640bddbd7c0f429f889c9ae4a97c15eaeda43cf382f1ad8248d2i0":{"status":200,"body":"\"a2626f7065666f756e64646e616d656b796f6c6f7072656e657572\""},"/inscriptions/block/146/0":{"status":200,"body":"{\"ids\":[\"f57fcba7f56588525dc5ac371b5fd2c8a5fdfd6b7b1be0c61e708abc543d79f6i0\",\"57b8455f43a8c008b1fc409d4476bdc9436a50d04ff216056a573d279dd9ced0i0\",\"71fd1fee2878d54817ab4b10bd702cf6f2184132010a94cc90aa33ff402e3a2ei0\"],\"more\":false,\"page_index\":0}"},"/inscription/f57fcba7f56588525dc5ac371b5fd2c8a5fdfd6b7b1be0c61e708abc543d79f6i0":{"status":200,"body":"{\"address\":\"bcrt1pjj02pwey8wd2cvhljhu9f03nar2h4ej9n08seaynam74zuknetjs42v47t\",\"charms\":[\"vindicated\"],\"child_count\":4,\"children\":[\"b1cbdb038ca472ab782eee8727138ad9e65598b22e3dcc5a1cfff85bd04fad63i0\",\"bb753b861520390c971a6a9f0c30bae3714f54aed13a3319b6e15c4ea11b81aci0\",\"05664f934dfdef1d84419b846dfafdf32aedf52f529977157fa7cfcae3ee4768i0\",\"be4fca82026629d2b26685ea090564ff0cf05535efab533358393ed4e8991ee7i0\"],\"content_length\":16,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":146,\"id\":\"f57fcba7f56588525dc5ac371b5fd2c8a5fdfd6b7b1be0c61e708abc543d79f6i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"57b8455f43a8c008b1fc409d4476bdc9436a50d04ff216056a573d279dd9ced0i0\",\"number\":57,\"parents\":[\"4a05a94d9148640bddbd7c0f429f889c9ae4a97c15eaeda43cf382f1ad8248d2i0\"],\"previous\":\"4a05a94d9148640bddbd7c0f429f889c9ae4a97c15eaeda43cf382f1ad8248d2i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"be4fca82026629d2b26685ea090564ff0cf05535efab533358393ed4e8991ee7:0:0\",\"timestamp\":1792100539,\"value\":1000}"},"/r/metadata/f57fcba7f56588525dc5ac371b5fd2c8a5fdfd6b7b1be0c61e708abc543d79f6i0":{"status":200,"body":"\"a3626f70656269727468646e616d656a77656c6c2d6b6e6f776e646b696e64636f7267\""},"/inscription/57b8455f43a8c008b1fc409d4476bdc9436a50d04ff216056a573d279dd9ced0i0":{"status":200,"body":"{\"address\":\"bcrt1psrxp7ee26r3e8u5z83g93a2pz2qapfadayzkag2n83mzwtf6yxnq4jgxcu\",\"charms\":[\"vindicated\"],\"child_count\":10,\"children\":[\"b04cf0f6ddc916ae6882ad7dfca69a774ea1c63759a1f4da732d2e4a8a8c1f95i0\",\"78cc3d00b8b0c564d5639c22a3b5c11ab45f81a4c464eee326338ffed762da3ai0\",\"eb0036a4f601d063a9f798437df1d6f7ba3a2874d43ed50b4beae42dd3c8c526i0\",\"42f006f3d0fbbb2eb2b26d2c49663f44b6c43786ba95eaa177dd3fcf974596c9i0\"],\"content_length\":10,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":146,\"id\":\"57b8455f43a8c008b1fc409d4476bdc9436a50d04ff216056a573d279dd9ced0i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"71fd1fee2878d54817ab4b10bd702cf6f2184132010a94cc90aa33ff402e3a2ei0\",\"number\":58,\"parents\":[\"4a05a94d9148640bddbd7c0f429f889c9ae4a97c15eaeda43cf382f1ad8248d2i0\"],\"previous\":\"f57fcba7f56588525dc5ac371b5fd2c8a5fdfd6b7b1be0c61e708abc543d79f6i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"b219a70dddf0979bb4c2e0ff3563ce56978d80352dd4358b7ce5bde77e0574e3:3:0\",\"timestamp\":1792100539,\"value\":1000}"},"/r/metadata/57b8455f43a8c008b1fc409d4476bdc9436a50d04ff216056a573d279dd9ced0i0":{"status":200,"body":"\"a3626f70656269727468646e616d656474696d65646b696e64636f7267\""},"/inscription/71fd1fee2878d54817ab4b10bd702cf6f2184132010a94cc90aa33Line truncated
|
||||
{"from":161,"ord":{"/blockheight":{"status":200,"body":"177"},"/inscriptions/block/161/0":{"status":200,"body":"{\"ids\":[],\"more\":false,\"page_index\":0}"},"/inscriptions/block/162/0":{"status":200,"body":"{\"ids\":[\"92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0\"],\"more\":false,\"page_index\":0}"},"/inscription/92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0":{"status":200,"body":"{\"address\":\"bcrt1p5fpqcc2wh3auczfq9hvyzxq5xj7cz052nedcq6af9x7rrwdmvl6qjnpr0p\",\"charms\":[],\"child_count\":4,\"children\":[\"aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0\",\"c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0\",\"1221a0bbe6de3ecda999d34fcd5d75bdd720f1b697b6c7d94e90c41cd328a585i0\",\"efb276d30bc86f9823b23eea5c66743124be2192ca4e20d65f8d1d8adba7f873i0\"],\"content_length\":17,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":162,\"id\":\"92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0\",\"number\":90,\"parents\":[],\"previous\":\"56ef20f78e26105e4261ad9b5ef634f4a94ad9d591c63c4e13034f35aa158cf0i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"efb276d30bc86f9823b23eea5c66743124be2192ca4e20d65f8d1d8adba7f873:0:0\",\"timestamp\":1792103427,\"value\":1000}"},"/r/metadata/92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0":{"status":200,"body":"\"a2626f7065666f756e64646e616d656b796f6c6f7072656e657572\""},"/inscriptions/block/163/0":{"status":200,"body":"{\"ids\":[\"aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0\",\"c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0\",\"1221a0bbe6de3ecda999d34fcd5d75bdd720f1b697b6c7d94e90c41cd328a585i0\"],\"more\":false,\"page_index\":0}"},"/inscription/aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0":{"status":200,"body":"{\"address\":\"bcrt1pdl8qt5zk5sy3d545s9lx2zcwy4y6s3jj8au3d0nauw5sr8smkccsg03dkr\",\"charms\":[\"vindicated\"],\"child_count\":4,\"children\":[\"1f2f6ec493f7a8f7c6d72441bdacc81f3abb60dcdb35f35c3432fb515a51469ci0\",\"695924409714b890b92372e464b10c3c6d3b476516b55bf6ce4578ec86558680i0\",\"04cac746c154c17a66191d2256130f13b8e55b91e5e9a5161336cc57367fd953i0\",\"ca510a40a8e7c5f1b90ebff2e9064c1a959d9039ae98e56536257ee5f9168884i0\"],\"content_length\":16,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":163,\"id\":\"aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0\",\"number\":91,\"parents\":[\"92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0\"],\"previous\":\"92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"ca510a40a8e7c5f1b90ebff2e9064c1a959d9039ae98e56536257ee5f9168884:1:0\",\"timestamp\":1792103427,\"value\":1000}"},"/r/metadata/aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0":{"status":200,"body":"\"a3626f70656269727468646e616d656a77656c6c2d6b6e6f776e646b696e64636f7267\""},"/inscription/c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0":{"status":200,"body":"{\"address\":\"bcrt1pd7jt377m7d89r83j2f54n4hw55a3ng3vje9xpwmfwe2rq0p9lxns8hpetf\",\"charms\":[\"vindicated\"],\"child_count\":10,\"children\":[\"dc3d8cad57a61fcca86a276689fb3e48626670ab0d1029d0a66053aee26b1706i0\",\"3a29612ca3f8be8e71a70ac74d289b722565d470f943dadb3fbca6acdf6494c8i0\",\"213e2c9a05ad8178eef5e118ec20830faf979c5bee33e1695ac662ca3ab700eai0\",\"896cff83a62fd915d08dd81f10244bc1cd9895c325f2eeb867eda956c00a4ddbi0\"],\"content_length\":10,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":163,\"id\":\"c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"1221a0bbe6de3ecda999d34fcd5d75bdd720f1b697b6c7d94e90c41cd328a585i0\",\"number\":92,\"parents\":[\"92391a738274c7eae301d110b126d6e594b4d2f94948caa6a0230bca063a2084i0\"],\"previous\":\"aa5251113a167df80eeded719c28573692764c34062a5d75a0d1bd18827761cdi0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"7226249689af6130a0aaf3713015dac8afcc04f388ff2eeabedb0c1a753af6ac:3:0\",\"timestamp\":1792103427,\"value\":1000}"},"/r/metadata/c93a6e2a424057e367e85c9c6e70dbaad1c542be9924e7f281c436e74b11defbi0":{"status":200,"body":"\"a3626f70656269727468646e616d656474696d65646b696e64636f7267\""},"/inscription/1221a0bbe6de3ecda999d34fcd5d75bdd720f1b697b6c7d94e90c4Line truncated
|
||||
@@ -51,7 +51,12 @@ test('real ord indexed the regtest rehearsal, and the same reducer folds it into
|
||||
// keys). Only the miner's order within blocks differs, a join and its adoption included.
|
||||
const { problems, reordered } = compareWithLab({ chain: new LocalChain(fixture.chain) }, events);
|
||||
assert.deepEqual(problems, []);
|
||||
assert.equal(reordered, 17);
|
||||
assert.equal(reordered, 15);
|
||||
// Belts ride their seats' own sats: on ord, each award shares its recipient's satpoint, through both trees' sales and
|
||||
// their splits, and is marked ♻️ reinscription (and ❤️🔥 vindicated: it was inscribed from the reveal's third input).
|
||||
const on = new Map(events.map(e => [e.id, e]));
|
||||
assert.deepEqual(s.log.filter(e => e.op === 'award').map(e => [s.nodes.get(e.meta.to).name, on.get(e.id).satpoint === on.get(e.meta.to).satpoint, on.get(e.id).charms]),
|
||||
[['sol', true, ['reinscription', 'vindicated']], ['luna', true, ['reinscription', 'vindicated']], ['scout', true, ['reinscription', 'vindicated']]]);
|
||||
});
|
||||
|
||||
test('a reveal commits to its exact script (BIP-341), and the lab still loads reveals made the old way', () => {
|
||||
|
||||
+5
-2
@@ -122,8 +122,11 @@ test('each holding has its own root, and every node holds its inscription at its
|
||||
});
|
||||
assert.match(a.taproot.address, /^bcrt1p/);
|
||||
assert.match(a.segwit.address, /^bcrt1q/);
|
||||
const award = act(lab, 'award', ops, { to: bot, skill: 'launch', belt: 'green' });
|
||||
assert.equal(at(lab, award), key(opsSeed, 0), 'an act is signed by its authorizer, and its record stays with that key');
|
||||
// A belt is signed for by its authorizer (ops, its parent) and rides its recipient's sat: one coin, one sat, at bot's key.
|
||||
const award = act(lab, 'award', ops, { to: bot, skill: 'launch', belt: 'green' }), coin = holderCoin(lab.chain, award);
|
||||
assert.equal(at(lab, award), key(opsSeed, 1), "a belt rides its recipient's sat");
|
||||
assert.equal(coin, holderCoin(lab.chain, bot));
|
||||
assert.equal(new Set(coin.inscriptions.map(i => i.sat)).size, 1);
|
||||
const other = act(lab, 'found', null, { name: 'other' });
|
||||
assert.notEqual(lab.keys.roots[other], m);
|
||||
});
|
||||
|
||||
@@ -46,8 +46,10 @@ test('found a holding, then birth, award, decide, graduate and close at the defa
|
||||
await page.locator('#award-to').selectOption({ label: 'acme-holdings / studio / bot (agent)' });
|
||||
await page.locator('#award-skill').fill('launch');
|
||||
await page.locator('#award-belt').selectOption('green');
|
||||
await expect(page.locator('#award-note')).toContainText("The belt is inscribed on bot's own sat, so it goes wherever bot goes and never sells apart from it");
|
||||
await expect(page.locator('#act-gas')).toHaveText("Gas: 1,000 sats from studio's tank (0.000075 tBTC), which tops up from its balance.");
|
||||
await submit(page, 'award');
|
||||
await expect(say(page)).toHaveText('bot holds a green belt in launch, at beat 6.');
|
||||
await expect(say(page)).toHaveText('bot holds a green belt in launch, at beat 6, on its own sat.');
|
||||
await verb(page, 'decide');
|
||||
await page.locator('#decide-title').fill('Ship weekly');
|
||||
await submit(page, 'decide');
|
||||
@@ -449,7 +451,7 @@ test('an agent pays a registry in another holding, and the registry awards it a
|
||||
await page.locator('#award-to').selectOption({ label: 'acme / scout (agent)' });
|
||||
await page.locator('#award-skill').fill('listen');
|
||||
await submit(page, 'award');
|
||||
await expect(say(page)).toHaveText('scout holds a white belt in listen, at beat 7.');
|
||||
await expect(say(page)).toHaveText('scout holds a white belt in listen, at beat 7, on its own sat.');
|
||||
await expect(page.locator('#timeline-list')).toContainText('award listen · white → scout, from well-known');
|
||||
await hold('acme').click();
|
||||
await expect(page.locator('#timeline-list')).toContainText('award listen · white → scout, from well-known');
|
||||
|
||||
Reference in new issue
Block a user