From 7c9306402ddd601c52bf034bc7163546483c4e09 Mon Sep 17 00:00:00 2001 From: teahubxyz Date: Thu, 15 Oct 2026 02:04:35 -0700 Subject: [PATCH] Orgchain v1.9: lineage/v0 as an ordinals metaprotocol, rehearsed on regtest SPEC.md writes lineage/v0 down as an ordinals metaprotocol: the envelope fields, chain order, authority by verified parents, every rule, ownership by sat. bin/lineage-index.mjs reads lineage events from a real ord server and folds them with the same reducer the lab uses. tools/regtest rehearses the sample on Bitcoin Core regtest with standardness on: the lab's own action code signs on a chain funded only by coins imported from regtest (LocalChain.importCoin), each beat's transactions are broadcast byte for byte and mined, ord indexes them, and --compare checks the same events in the same beats, the same metadata and parents, every inscription in the same output and the same trees. tests/indexer.test.js replays a recorded run. The first rehearsal found that reveals committed to tapscript's re-encoded tapleaf, which real Bitcoin rejects. They now commit to the exact script bytes, and the local chain checks script paths per BIP-341/342, still loading reveals made before. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01H4upF5LVwmPDDqrhdUfnXh --- .gitignore | 1 + AGENTS.md | 13 ++-- PROVENANCE.md | 2 +- README.md | 24 +++++- SPEC.md | 108 ++++++++++++++++++++++++++ bin/lineage-index.mjs | 29 +++++++ src/adapters/chain.js | 57 ++++++++++++-- src/adapters/inscription.js | 6 +- src/adapters/metadata-index.js | 4 +- src/adapters/sats.js | 4 + src/indexer/compare.js | 33 ++++++++ src/indexer/ord.js | 56 +++++++++++++ src/orgchain/actions.js | 3 +- src/orgchain/money.js | 2 +- tests/fixtures/regtest-rehearsal.json | 1 + tests/indexer.test.js | 78 +++++++++++++++++++ tools/regtest/README.md | 46 +++++++++++ tools/regtest/bitcoin.conf | 13 ++++ tools/regtest/record.mjs | 16 ++++ tools/regtest/rehearse.mjs | 107 +++++++++++++++++++++++++ tools/regtest/rpc.mjs | 20 +++++ 21 files changed, 602 insertions(+), 21 deletions(-) create mode 100644 SPEC.md create mode 100644 bin/lineage-index.mjs create mode 100644 src/indexer/compare.js create mode 100644 src/indexer/ord.js create mode 100644 tests/fixtures/regtest-rehearsal.json create mode 100644 tests/indexer.test.js create mode 100644 tools/regtest/README.md create mode 100644 tools/regtest/bitcoin.conf create mode 100644 tools/regtest/record.mjs create mode 100644 tools/regtest/rehearse.mjs create mode 100644 tools/regtest/rpc.mjs diff --git a/.gitignore b/.gitignore index c5a0ba3..770d204 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ dist/ test-results/ playwright-report/ .env* +tools/regtest/out/ diff --git a/AGENTS.md b/AGENTS.md index 25eacdc..5c4c4c7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,13 +3,16 @@ Orgchain is lineage's app for creating and operating lineage holdings on Super Testnet's toy chain. - Test-only, always: no mainnet funds, no hidden signing, no custody, and nothing presented as a real key. -- Lineage semantics live in one place, `src/orgchain/lineage.js`. The UI never interprets events itself, and every act - is vetted by that reducer before it is signed. A new op means a reducer change, its tests and the README's table. +- Lineage semantics live in one place, `src/orgchain/lineage.js`, and `SPEC.md` says the same in words. The UI never + interprets events itself, and every act is vetted by that reducer before it is signed. A new op or rule means a + reducer change, its tests, the README's table and `SPEC.md`. +- Whatever signs must stay valid on real Bitcoin. After changing how transactions or envelopes are built, rerun the + regtest rehearsal and its comparison (`tools/regtest/README.md`) and refresh the fixture it records. - The engine (`src/adapters`, `src/upstream`, `src/storage.js`, `src/miner.js`) is Super Testnet's. Compose with it rather than rewriting it, and keep its tests passing. Preserve upstream licenses and provenance. Its local - changes are P2WPKH payments, a coinbase split across up to 64 outputs, and ordinary spends of up to eight inputs - and outputs whose key-path signatures may carry 0x83, all in `src/adapters/chain.js` (PROVENANCE.md); keep them - small and tested. + changes are P2WPKH payments, a coinbase split across up to 64 outputs, ordinary spends of up to eight inputs and + outputs whose key-path signatures may carry 0x83, coins imported from a real chain, and BIP-341/342 checks of + reveals' script paths, all in `src/adapters/chain.js` (PROVENANCE.md); keep them small and tested. - Money lives in `src/orgchain/money.js`: beats pay the holdings (split by hashrate, or all to the last act; under Mainnet none mine, and the lab wallet funds them), sends spend SegWit coins only, so an inscription can never leave as change, and balances are read off the chain, never stored. Gas (`src/orgchain/actions.js`): every act is paid by diff --git a/PROVENANCE.md b/PROVENANCE.md index ad2c401..c1ff598 100644 --- a/PROVENANCE.md +++ b/PROVENANCE.md @@ -20,7 +20,7 @@ Runtime dependencies are pinned in `package.json` and `package-lock.json`: - **@scure/bip32 1.7.0**, Patricio Palladino and Paul Miller, [upstream](https://github.com/paulmillr/scure-bip32), MIT. Used for HD derivation under the BIP-85, BIP-86, BIP-84 and NIP-06 paths. License retained at [`public/licenses/scure-bip32.txt`](public/licenses/scure-bip32.txt). - **@scure/bip39 1.6.0**, Patricio Palladino and Paul Miller, [upstream](https://github.com/paulmillr/scure-bip39), MIT. Used for the 24-word lab roots and their seeds. License retained at [`public/licenses/scure-bip39.txt`](public/licenses/scure-bip39.txt). -Three local changes to the engine, all original code in `src/adapters/chain.js`, tested in `tests/money.test.js`, with Super Testnet's tests (`tests/adapters.test.js`) passing unchanged. Orgchain v1.5: the chain also accepts native SegWit (P2WPKH) outputs and SIGHASH_ALL P2WPKH spends, verified per BIP-143, so lineage accounts can hold and send test BTC at their BIP-84 addresses; everything about inscriptions stays Taproot-only. Orgchain v1.6: a coinbase may split the block reward across up to 64 outputs (`mineTo`; `mine` still pays one script), so one beat can pay several holdings. Orgchain v1.7: ordinary (non-reveal) spends may take up to eight inputs and outputs (`MAX_IO`), and their key-path Taproot signatures may carry SIGHASH_SINGLE|ANYONECANPAY (0x83, the sell-order flag), so one transaction can carry a deal: an item, the buyer's coins, the price and change. Reveals keep their two-in, two-out shape and default signatures. +Five local changes to the engine, all original code in `src/adapters/chain.js`, tested in `tests/money.test.js`, with Super Testnet's tests (`tests/adapters.test.js`) passing unchanged. Orgchain v1.5: the chain also accepts native SegWit (P2WPKH) outputs and SIGHASH_ALL P2WPKH spends, verified per BIP-143, so lineage accounts can hold and send test BTC at their BIP-84 addresses; everything about inscriptions stays Taproot-only. Orgchain v1.6: a coinbase may split the block reward across up to 64 outputs (`mineTo`; `mine` still pays one script), so one beat can pay several holdings. Orgchain v1.7: ordinary (non-reveal) spends may take up to eight inputs and outputs (`MAX_IO`), and their key-path Taproot signatures may carry SIGHASH_SINGLE|ANYONECANPAY (0x83, the sell-order flag), so one transaction can carry a deal: an item, the buyer's coins, the price and change. Reveals keep their two-in, two-out shape and default signatures. Orgchain v1.9: a chain may start from coins imported from a real chain (`importCoin`; `MetadataIndex` numbers their sats the same way, `importRanges` in `src/adapters/sats.js`), so the regtest rehearsal can broadcast the lab's transactions byte for byte; and a reveal's script path is checked per BIP-341 and BIP-342 against the exact script bytes (`tapscriptValid`), with tapscript's own check kept as a fallback so labs saved earlier still load. That rehearsal also found that the inscription adapter (`src/adapters/inscription.js`, adapted from inscriptions-online) committed its reveals to tapscript's `encodeScript` of a hex script, which re-encodes one-byte pushes, so real Bitcoin rejected them; it now commits to the exact bytes (`tapleafOf`). Tested in `tests/indexer.test.js`. Vite and Playwright are build/test dependencies, not runtime network services. Their installed package notices remain in the locked dependency installation. No unlicensed upstream code was copied. diff --git a/README.md b/README.md index d61d3a3..f00c287 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,26 @@ reused. The metadata says what an event means; the parent it spends proves who w 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 + +`SPEC.md` defines `lineage/v0` as an ordinals metaprotocol: what each field of the envelope means, the order events +fold in, why a parent proves authority, and every rule the reducer applies. The lab is one implementation. The other is +`bin/lineage-index.mjs`, which reads the events from a real `ord` server and folds them with the same reducer: + +``` +node bin/lineage-index.mjs --ord http://127.0.0.1:8088 [--from ] [--compare ] +``` + +`tools/regtest/` rehearses the sample 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). + ## Build and test ``` npm ci -npm test # engine, reducer, lab and money (node --test) +npm test # engine, reducer, lab, money and the ord indexer (node --test) npm run build # dist/ is the site; relative paths, so it works at any sub-path npm run test:e2e # Playwright on vite preview; PW_CHANNEL=chrome uses installed Chrome ``` @@ -99,11 +114,12 @@ Deploy: copy `dist/` to `ventures/lineage/orgchain/` on `teahubxyz/time` `gh-pag ## Built on Super Testnet The chain, wallet, inscriptions, metadata and index (`src/adapters`, `src/upstream`, with `src/storage.js` and -`src/miner.js`) are [Super Testnet](https://supertestnet.lol)'s, with their tests (`tests/adapters.test.js`) and three +`src/miner.js`) are [Super Testnet](https://supertestnet.lol)'s, with their tests (`tests/adapters.test.js`) and five additions: the chain also moves native SegWit (P2WPKH) payments, BIP-143 signatures included; a coinbase may split -its reward across up to 64 outputs; and ordinary spends take up to eight inputs and outputs, whose key-path +its reward across up to 64 outputs; ordinary spends take up to eight inputs and outputs, whose key-path signatures may carry SIGHASH_SINGLE|ANYONECANPAY (0x83, the sell-order flag), while reveals keep their two-in, -two-out shape (`tests/money.test.js`). +two-out shape (`tests/money.test.js`); a chain may start from coins imported from a real chain; and reveals' +script paths are checked per BIP-341 and BIP-342 (`tests/indexer.test.js`). See `PROVENANCE.md` and `public/licenses/`. Lineage's key derivation and the claim and countersign handoff (`src/adapters/lineage-keys.js`, `src/adapters/handoff.js`, with their spec vectors in `tests/keys.test.js`) come from the `tree` branch. The full lab (explorer, delegates, recursion and diff --git a/SPEC.md b/SPEC.md new file mode 100644 index 0000000..739ca64 --- /dev/null +++ b/SPEC.md @@ -0,0 +1,108 @@ +# 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. + +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. + +## 1. Inscriptions + +lineage/v0 uses these fields of the ord envelope ([inscriptions](https://docs.ordinals.com/inscriptions.html)): + +| tag | field | use | +| --- | --- | --- | +| 7 | metaprotocol | exactly `lineage/v0`; inscriptions without it are not lineage's | +| 5 | metadata | a CBOR map: `op` and its fields (section 4), all strings, in 520-byte pushes (Orgchain keeps it under 2048 bytes) | +| 3 | parent | the inscription id of the node that authorizes the event; exactly one, except for `found` | +| 1 | content type | `text/plain;charset=utf-8`; the body is a short label such as `birth time`, ignored by the rules | +| 2 | pointer | puts a child's inscription in its own output when the parent is spent first | + +The body, delegate (tag 11) and properties (tag 17) carry no meaning here; properties may later carry display traits +such as a belt's colour, never authority. Unknown odd tags are ignored. Never use unknown even tags: ord treats such an +inscription as unbound, so it has no location. + +A reveal is a script-path spend whose tapleaf commits to the envelope script's exact bytes (BIP-341). Orgchain's reveals +before v1.9 committed to a re-encoded script; real Bitcoin rejects those, and Orgchain's local chain still loads them. + +## 2. Order + +Events fold in chain order: block height, then transaction order in the block, then envelope order in the transaction. +ord's inscription numbers follow the same order. Unconfirmed events are provisional, and ord indexes none. + +Within a block, the miner sets the order. Events that share an authority are chained, because each one spends that +authority's inscription, so their order is fixed. Independent events may land in any order, and outside the conflicts +in section 6 that changes nothing. + +## 3. Nodes and authority + +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. + +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)). +Indexers use ord's recorded parents, never the raw tag. So acting for a node means holding its inscription, and moving +the inscription moves the authority. + +## 4. Events + +| op | parent | metadata | effect | +| --- | --- | --- | --- | +| `found` | none | `name` | a holding; no active holding may already have that name | +| `birth` | an active holding or org | `name`, `kind` (`org` or `agent`, default `org`) | a child; the parent may not already have an active child of that name | +| `award` | an active holding or org | `to`, `skill`, `belt` (`white`, `green` or `black`) | `to` (any other active node, in any holding) holds that belt in that skill, replacing its earlier belt in it | +| `decide` | an active node | `title` (required), `why` (optional) | a decision on the node's record | +| `make` | an active node | `name`, `note` (optional) | an item, made and first held by the node; a node makes one item per name | +| `graduate` | the child's parent, active | `of` (an active org child, not yet adopted) | the edge becomes adopted, and the org 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 | + +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. Agents do not birth or award. An event that +breaks a rule is rejected: it stays on the chain and changes nothing. Any other `op` is rejected. + +## 5. Holdings and graduation + +A holding is a root: a `found`, or an org that graduated. When an org graduates, it and its descendants still in the +old holding re-root under it, and it joins the holdings. Its parent stays its parent, so where it came from stays on the +record, and its old holding's tree keeps a row for it. A graduation event belongs to both holdings. + +In Orgchain a graduation is followed by a handoff: the org's fresh root claims its inscription, and the derived key that +held it countersigns. After that, only the new root can act for the org. + +## 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. + +## 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. + +## 8. Keys + +Indexers need no keys. Keys say where a node's inscription is expected to live, and who can sign for it. Each holding +has a 24-word root (BIP-39), and an org's seed is BIP-85 from its parent's (`m/83696968'/39'/0'/24'/i'`) until it +graduates. An org is account 0 of its seed and its agents are accounts 1 to n. Each account holds its inscription at its +BIP-86 Taproot address (`m/86'/coin'/account'/0/0`), keeps its money at BIP-84 SegWit, and has a NIP-06 npub. + +## 9. Versions and other chains + +Tag 7 carries the version. A new version is a new string with its own rules, which v0 indexers ignore. The events and +the rules are chain-agnostic. What another chain must supply is its own proof of order and of authority (a signature +by the node's key where nothing is spent). Each holding's events would live on one home chain, and an org-signed +keyring would bind a node's keys across chains. + +## 10. Conformance + +`tools/regtest` rehearses Orgchain's sample on Bitcoin Core regtest, byte for byte, 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 every inscription in the same output. `tests/indexer.test.js` replays a recorded run. diff --git a/bin/lineage-index.mjs b/bin/lineage-index.mjs new file mode 100644 index 0000000..ee398c7 --- /dev/null +++ b/bin/lineage-index.mjs @@ -0,0 +1,29 @@ +#!/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 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 ] [--compare ] +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})`); +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; +} diff --git a/src/adapters/chain.js b/src/adapters/chain.js index 959fee4..ecb3d2d 100644 --- a/src/adapters/chain.js +++ b/src/adapters/chain.js @@ -1,9 +1,9 @@ -import { outputRanges, satAt, offsetOf } from './sats.js'; +import { importRanges, outputRanges, satAt, offsetOf } from './sats.js'; import { Script, Signer, Tx } from '@cmdcode/tapscript'; import { sha256 } from '@noble/hashes/sha2'; import { ripemd160 } from '@noble/hashes/legacy'; -import { secp256k1 } from '@noble/curves/secp256k1'; -import { bytesToHex, hexToBytes } from '@noble/curves/abstract/utils'; +import { schnorr, secp256k1 } from '@noble/curves/secp256k1'; +import { bytesToHex, bytesToNumberBE, concatBytes, hexToBytes } from '@noble/curves/abstract/utils'; import { getVout } from '../upstream/primitives.js'; import { readEnvelope, POSTAGE, FEE } from './inscription.js'; import { NETWORK } from './wallet.js'; @@ -29,19 +29,58 @@ function segwitValid(tx, i) { return secp256k1.verify(sig.slice(0, -2), Signer.segwit.hash(tx, i, { pubkey, sigflag: 0x01 }), pubkey, { lowS: true, format: 'der' }); } +// BIP-341 and BIP-342 for a reveal's script path: the control block commits the script's exact bytes to the spent +// output's key, and the script's key signs for that leaf. Orgchain v1.9, after real Bitcoin rejected reveals that +// tapscript's own check had passed: it hashes a re-encoded script, not the bytes in the witness. +const tagged = (tag, ...parts) => { const t = sha256(new TextEncoder().encode(tag)); return sha256(concatBytes(t, t, ...parts)); }; +const compactSize = n => (n < 0xfd ? Uint8Array.of(n) : Uint8Array.of(0xfd, n & 255, n >> 8)); +export function tapscriptValid(tx, i) { + const [sig, script, control] = tx.vin[i].witness, cb = hexToBytes(control), bytes = hexToBytes(script); + if (cb.length < 33 || (cb.length - 33) % 32 || bytes.length > 0xffff || !/^20[0-9a-f]{64}ac/.test(script)) return false; + const leaf = tagged('TapLeaf', Uint8Array.of(cb[0] & 0xfe), compactSize(bytes.length), bytes); + let node = leaf; + for (let j = 33; j < cb.length; j += 32) { + const other = cb.slice(j, j + 32), first = bytesToHex(node) <= bytesToHex(other); + node = tagged('TapBranch', first ? node : other, first ? other : node); + } + const tweak = bytesToNumberBE(tagged('TapTweak', cb.slice(1, 33), node)); + if (tweak >= secp256k1.CURVE.n) return false; + const key = secp256k1.ProjectivePoint.fromHex(`02${bytesToHex(cb.slice(1, 33))}`).add(secp256k1.ProjectivePoint.BASE.multiply(tweak)).toAffine(); + if (`5120${key.x.toString(16).padStart(64, '0')}` !== scriptHex(tx.vin[i].prevout.scriptPubKey) || Number(key.y & 1n) !== (cb[0] & 1)) return false; + return schnorr.verify(sig, Signer.taproot.hash(tx, i, { extension: bytesToHex(leaf) }), script.slice(2, 66)); +} + // Original, intentionally narrow implementation of testnet_generator's local UTXO/mining model. // This is NOT a Bitcoin consensus engine. Only our key-path and ord MIME templates, and P2WPKH payments, are accepted. export class LocalChain { constructor(snapshot) { - this.blocks = []; this.mempool = []; this.utxos = new Map(); this.transactions = new Map(); + this.blocks = []; this.mempool = []; this.utxos = new Map(); this.transactions = new Map(); this.imports = []; if (snapshot) { assert(snapshot.network === NETWORK && snapshot.version === 1, 'Unsupported local chain version/network.'); - assert(Array.isArray(snapshot.blocks) && Array.isArray(snapshot.mempool), 'Invalid chain snapshot.'); + assert(Array.isArray(snapshot.blocks) && Array.isArray(snapshot.mempool) && Array.isArray(snapshot.imports ?? []), 'Invalid chain snapshot.'); + for (const coin of snapshot.imports ?? []) this.importCoin(coin); for (const block of snapshot.blocks) this.acceptBlock(structuredClone(block)); this.submitPackage(snapshot.mempool); } } - snapshot() { return { network: NETWORK, version: 1, blocks: structuredClone(this.blocks), mempool: [...this.mempool] }; } + snapshot() { + return { network: NETWORK, version: 1, blocks: structuredClone(this.blocks), mempool: [...this.mempool], + ...(this.imports.length ? { imports: structuredClone(this.imports) } : {}) }; + } + // Orgchain: a coin that already exists on a real chain (regtest, say), taken as given like a genesis allocation, so + // the lab's own transactions can spend it and then be broadcast there byte for byte. Nothing here checks that chain. + importCoin({ txid, vout, value, script }) { + assert(/^[0-9a-f]{64}$/.test(txid) && Number.isSafeInteger(vout) && vout >= 0 && Number.isSafeInteger(value) && value >= 330 && value <= REWARD && + (TAPROOT.test(script) || P2WPKH.test(script)), 'Invalid imported coin.'); + assert(!this.utxos.has(key(txid, vout)) && !this.transactions.has(txid) && !this.imported(txid, vout), 'That coin is already in this chain.'); + this.utxos.set(key(txid, vout), { txid, vout, value, scriptPubKey: script, height: 0, ranges: importRanges(this.imports.length, value), inscriptions: [] }); + this.imports.push({ txid, vout, value, script }); + } + // What an imported coin was, as a spent output reads: { value, scriptPubKey }. + imported(txid, vout) { + const coin = this.imports.find(c => c.txid === txid && c.vout === vout); + return coin ? { value: coin.value, scriptPubKey: coin.script } : null; + } coins(script) { return [...this.utxos.values()].filter(c => scriptHex(c.scriptPubKey) === scriptHex(script)); } satLocation(sat) { for (const coin of this.utxos.values()) { const offset = offsetOf(coin.ranges, sat); if (offset !== null) return { txid: coin.txid, vout: coin.vout, offset }; } @@ -115,7 +154,11 @@ export class LocalChain { } tx.vin.forEach((input, i) => { let valid = false; - try { valid = P2WPKH.test(scriptHex(coins[i].scriptPubKey)) ? segwitValid(tx, i) : Signer.taproot.verify(tx, i, i === scriptInput ? config : {}); } catch { /* Normalize malformed signatures. */ } + // A reveal from before v1.9 committed to tapscript's re-encoded leaf; the lab still loads it, and makes BIP-341 ones. + try { + valid = P2WPKH.test(scriptHex(coins[i].scriptPubKey)) ? segwitValid(tx, i) + : i === scriptInput ? tapscriptValid(tx, i) || Signer.taproot.verify(tx, i, config) : Signer.taproot.verify(tx, i, {}); + } catch { /* Normalize malformed signatures. */ } assert(valid, 'Invalid signature or Taproot commitment.'); }); diff --git a/src/adapters/inscription.js b/src/adapters/inscription.js index 2778edb..c87bf43 100644 --- a/src/adapters/inscription.js +++ b/src/adapters/inscription.js @@ -62,6 +62,10 @@ export function envelope(pubkey, value, parent = null, metadata = undefined, opt push('05') + push(hex(metadataBytes.slice(i * 520, (i + 1) * 520)))).join('')) + (metaprotocolBytes ? push('07') + push(hex(metaprotocolBytes)) : '') + '00' + chunks.map(push).join('') + '68'; } +// BIP-341: a tapleaf commits to its script's exact bytes (leaf version, compact size, script). tapscript's encodeScript +// re-encodes a hex script and turns one-byte pushes into numbers, so it committed to bytes the witness never shows, and +// real Bitcoin rejected those reveals (found by the regtest rehearsal; the local chain still loads the ones made so). +export const tapleafOf = script => Tap.tree.getLeaf(Script.encode(script, true)); export function readEnvelope(witness) { if (!Array.isArray(witness) || witness.length !== 3) throw new Error('Unsupported script-path witness.'); const parts = Script.decode(witness[1]); @@ -120,7 +124,7 @@ export class InscriptionAdapter { const carriedItem = reinscribe?.inscriptions.find(i => i.id === options.reinscribe); const pointer = carriedItem ? this.chain.satLocation(carriedItem.sat).offset : options.pointer ?? (parent ? parent.value : undefined); const script = envelope(this.wallet.pubkey, payload, parentId, metadata, { ...options, pointer }); - const leaf = Tap.encodeScript(script); + const leaf = tapleafOf(script); const [tapkey, control] = Tap.getPubKey(this.wallet.pubkey, { target: leaf }); const commit = Tx.create({ vin: [getVin(coin.txid, coin.vout, coin.value, coin.scriptPubKey)], diff --git a/src/adapters/metadata-index.js b/src/adapters/metadata-index.js index ddea79f..2e48ac2 100644 --- a/src/adapters/metadata-index.js +++ b/src/adapters/metadata-index.js @@ -1,4 +1,4 @@ -import { outputRanges, satAt, offsetOf } from './sats.js'; +import { importRanges, outputRanges, satAt, offsetOf } from './sats.js'; import { Tx } from '@cmdcode/tapscript'; import { readEnvelope } from './inscription.js'; @@ -8,6 +8,8 @@ export class MetadataIndex { constructor(chain) { this.events = []; this.utxos = new Map(); + // Coins imported from a real chain start out known, with the same sat numbers the chain gave them. + (chain.imports ?? []).forEach((c, i) => this.utxos.set(`${c.txid}:${c.vout}`, { txid: c.txid, vout: c.vout, value: c.value, ranges: importRanges(i, c.value) })); const visit = (raw, height, block) => { const tx = Tx.decode(raw); const txid = Tx.util.getTxid(tx), coinbase = tx.vin[0].txid === '0'.repeat(64); diff --git a/src/adapters/sats.js b/src/adapters/sats.js index 7bc7231..22e5d58 100644 --- a/src/adapters/sats.js +++ b/src/adapters/sats.js @@ -1,6 +1,10 @@ // Synthetic local numbering: each height issues 5e9 consecutive sats. Fees retire // the unassigned input tail (not Bitcoin miner fee collection). Half-open ranges. export const LOCAL_REWARD = 5e9; +// Orgchain: coins imported from a real chain (LocalChain.importCoin) number their sats from 2^52 up, one 5e9 block of +// numbers each, well clear of every local coinbase's. +export const IMPORT_BASE = 2 ** 52; +export const importRanges = (index, value) => [[IMPORT_BASE + index * LOCAL_REWARD, IMPORT_BASE + index * LOCAL_REWARD + value]]; export function sliceRanges(ranges, offset, length) { const result = []; for (const [start, end] of ranges) { diff --git a/src/indexer/compare.js b/src/indexer/compare.js new file mode 100644 index 0000000..f1063e1 --- /dev/null +++ b/src/indexer/compare.js @@ -0,0 +1,33 @@ +// Does an ord index tell the same lineage story as a lab? The same events, each in the same beat, with the same +// metadata and parent; the same folded state; and every inscription in the same output. Within a block the miner sets +// the order, so independent events (different authorities) may land in another order than the lab cued them; events +// that share an authority are chained by spending it, so theirs is fixed. The comparison therefore keys everything by +// inscription id and counts reorderings rather than failing on them. Used by bin/lineage-index.mjs --compare. +import { MetadataIndex } from '../adapters/metadata-index.js'; +import { holderCoin } from '../adapters/handoff.js'; +import { lineageEvents, reduce } from '../orgchain/lineage.js'; +import { asBeats } from './ord.js'; + +const byId = list => [...list].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)); +const folded = state => JSON.stringify({ + roots: state.roots.map(r => r.id).sort(), nodes: byId(state.nodes.values()), items: byId(state.items.values()), + log: byId(state.log.map(e => ({ id: e.id, op: e.op, ok: e.ok, why: e.why, node: e.node, root: e.root, roots: e.roots, also: e.also }))), +}); + +export function compareWithLab(lab, events) { + const mine = asBeats(lineageEvents(new MetadataIndex(lab.chain)).filter(e => e.status === 'confirmed')), theirs = asBeats(events); + const labById = new Map(mine.map(e => [e.id, e])), problems = []; + for (const e of theirs) { + const l = labById.get(e.id); + if (!l) { problems.push(`${e.id}: on ord, not in the lab`); continue; } + if (l.height !== e.height) problems.push(`${e.id}: beat ${e.height} on ord, ${l.height} in the lab`); + if (JSON.stringify(l.metadata) !== JSON.stringify(e.metadata)) problems.push(`${e.id}: its metadata differs`); + if ((l.parent ?? null) !== e.parent || e.parents > 1) problems.push(`${e.id}: its parent differs (lab ${l.parent ?? 'none'}, ord ${e.parent ?? 'none'})`); + const coin = holderCoin(lab.chain, e.id), at = coin && `${coin.txid}:${coin.vout}`; + if (at !== e.location) problems.push(`${e.id}: it sits at ${e.location} on ord and ${at} in the lab`); + } + for (const l of mine) if (!theirs.some(e => e.id === l.id)) problems.push(`${l.id}: in the lab, not on ord`); + if (folded(reduce(mine)) !== folded(reduce(theirs))) problems.push('the folded states differ'); + const reordered = theirs.filter((e, i) => mine[i]?.id !== e.id).length, state = reduce(events); + return { problems, reordered, events: events.length, nodes: state.nodes.size, items: state.items.size }; +} diff --git a/src/indexer/ord.js b/src/indexer/ord.js new file mode 100644 index 0000000..a9ef639 --- /dev/null +++ b/src/indexer/ord.js @@ -0,0 +1,56 @@ +// lineage/v0 over a real ord index: the inscriptions ord has seen become events for the same reducer the lab uses +// (../orgchain/lineage.js). Meaning comes from each inscription's metaprotocol (tag 7) and CBOR metadata (tag 5); +// authority from its parents as ord recorded them, and ord records a parent only when the inscribing transaction spent +// it (docs.ordinals.com, provenance). ord indexes confirmed blocks only, so nothing here is provisional. SPEC.md has +// the rules; this file only reads. Endpoints: /blockheight, /inscriptions/block//, /inscription/, +// /r/metadata/, all from ord's JSON and recursive APIs. +import { hexToBytes } from '@noble/hashes/utils'; +import { decodeMetadata } from '../adapters/metadata.js'; +import { PROTOCOL, reduce } from '../orgchain/lineage.js'; + +// A GET against an ord server; null for 404. fetchImpl lets tests replay recorded answers. +export function ordClient(base, fetchImpl = globalThis.fetch) { + const root = String(base).replace(/\/+$/, ''); + return async (path, { json = true } = {}) => { + const res = await fetchImpl(`${root}${path}`, { headers: { Accept: json ? 'application/json' : 'text/plain' } }); + if (res.status === 404) return null; + if (!res.ok) throw new Error(`ord ${path}: HTTP ${res.status}`); + return json ? res.json() : res.text(); + }; +} + +// One inscription as a lineage event, or null when it isn't lineage's. Where it sits now rides along, for holders. +export async function ordEvent(get, id) { + const ins = await get(`/inscription/${id}`); + if (!ins || ins.metaprotocol !== PROTOCOL) return null; + const hex = await get(`/r/metadata/${id}`); + return { + id, number: ins.number, status: 'confirmed', height: ins.height, protocol: PROTOCOL, + metadata: hex ? decodeMetadata(hexToBytes(hex)) : null, + parent: ins.parents?.[0] ?? null, parents: ins.parents?.length ?? 0, + location: ins.satpoint ? ins.satpoint.split(':').slice(0, 2).join(':') : null, address: ins.address ?? null, + }; +} + +// Every lineage/v0 event ord has indexed from one height up, in chain order (inscription numbers follow it). +export async function ordEvents(get, { from = 0, to = null } = {}) { + const top = to ?? Number(await get('/blockheight', { json: false })); + const events = []; + for (let height = from; height <= top; height++) { + for (let page = 0; ; page++) { + const found = await get(`/inscriptions/block/${height}/${page}`); + for (const id of found?.ids ?? []) { const e = await ordEvent(get, id); if (e) events.push(e); } + if (!found?.more) break; + } + } + return events.sort((a, b) => a.number - b.number); +} + +// Heights as beat ordinals (1, 2, 3…), so two chains that carried the same history in the same blocks fold alike. +export function asBeats(events) { + const beats = new Map([...new Set(events.map(e => e.height))].sort((a, b) => a - b).map((h, i) => [h, i + 1])); + return events.map(e => ({ ...e, height: beats.get(e.height) })); +} + +// The lineage state ord's index implies. Holders are read off the chain like everything else. +export const ordState = events => reduce(events); diff --git a/src/orgchain/actions.js b/src/orgchain/actions.js index 73c390d..3bef487 100644 --- a/src/orgchain/actions.js +++ b/src/orgchain/actions.js @@ -116,7 +116,8 @@ export function lastHolding(lab) { if (record?.inscription) { const root = state.log.find(e => e.id === record.inscription.id)?.root; if (root) return root; continue; } // The first input a node signed: a send's sender, or a deal's holder, who accepted it. for (const input of record?.tx.vin ?? []) { - const prev = lab.chain.transactions.get(input.txid)?.tx.vout[input.vout], root = prev && byScript.get(scriptHex(prev.scriptPubKey)); + const prev = lab.chain.transactions.get(input.txid)?.tx.vout[input.vout] ?? lab.chain.imported(input.txid, input.vout); + const root = prev && byScript.get(scriptHex(prev.scriptPubKey)); if (root) return root; } } diff --git a/src/orgchain/money.js b/src/orgchain/money.js index 5079cf5..4440346 100644 --- a/src/orgchain/money.js +++ b/src/orgchain/money.js @@ -217,7 +217,7 @@ export function ledger(chain, owner, tank = new Map(), deals = new Map()) { continue; } const ins = tx.vin.map(i => { - const prev = chain.transactions.get(i.txid)?.tx.vout[i.vout]; + const prev = chain.transactions.get(i.txid)?.tx.vout[i.vout] ?? chain.imported?.(i.txid, i.vout); return prev ? { node: who(prev.scriptPubKey), value: Number(prev.value) } : null; }); const deal = deals.get(txid); diff --git a/tests/fixtures/regtest-rehearsal.json b/tests/fixtures/regtest-rehearsal.json new file mode 100644 index 0000000..c86b447 --- /dev/null +++ b/tests/fixtures/regtest-rehearsal.json @@ -0,0 +1 @@ +{"from":120,"ord":{"/blockheight":{"status":200,"body":"131"},"/inscriptions/block/120/0":{"status":200,"body":"{\"ids\":[],\"more\":false,\"page_index\":0}"},"/inscriptions/block/121/0":{"status":200,"body":"{\"ids\":[],\"more\":false,\"page_index\":0}"},"/inscriptions/block/122/0":{"status":200,"body":"{\"ids\":[],\"more\":false,\"page_index\":0}"},"/inscriptions/block/123/0":{"status":200,"body":"{\"ids\":[\"ee6cabd275c53011248727e064c0be3841799b7391bc337520d9aac1ebdc60d1i0\"],\"more\":false,\"page_index\":0}"},"/inscription/ee6cabd275c53011248727e064c0be3841799b7391bc337520d9aac1ebdc60d1i0":{"status":200,"body":"{\"address\":\"bcrt1pmhndmcwyd3qt3lzq7x2fyxa62r8fpj9rn4gr05un6fxt35ft2uzs3tdaf7\",\"charms\":[],\"child_count\":3,\"children\":[\"ac1d4d0cfaeb19d99bc4c6e829d64100b598f2a3cfa538348f230ae3fe09e685i0\",\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\",\"c7d79e81fb104160f3843649d958ab9f56d49df4390fb02b93828021d8dd34dci0\"],\"content_length\":17,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":123,\"id\":\"ee6cabd275c53011248727e064c0be3841799b7391bc337520d9aac1ebdc60d1i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"ac1d4d0cfaeb19d99bc4c6e829d64100b598f2a3cfa538348f230ae3fe09e685i0\",\"number\":0,\"parents\":[],\"previous\":null,\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"c7d79e81fb104160f3843649d958ab9f56d49df4390fb02b93828021d8dd34dc:0:0\",\"timestamp\":1792054032,\"value\":1000}"},"/r/metadata/ee6cabd275c53011248727e064c0be3841799b7391bc337520d9aac1ebdc60d1i0":{"status":200,"body":"\"a2626f7065666f756e64646e616d656b796f6c6f7072656e657572\""},"/inscriptions/block/124/0":{"status":200,"body":"{\"ids\":[\"ac1d4d0cfaeb19d99bc4c6e829d64100b598f2a3cfa538348f230ae3fe09e685i0\",\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\"],\"more\":false,\"page_index\":0}"},"/inscription/ac1d4d0cfaeb19d99bc4c6e829d64100b598f2a3cfa538348f230ae3fe09e685i0":{"status":200,"body":"{\"address\":\"bcrt1p436wl2jp50ceq825lm0em2mtvyafeqvf09ggsa9ku6z9rusqat7q40jzp5\",\"charms\":[\"vindicated\"],\"child_count\":4,\"children\":[\"e29b26c17d48c09e72109ae42a833fb24e000cf89790ebbaebea6d3a884fc1bdi0\",\"3d9b514edc1956d5f4fef3edec7267c4920ce81449b8c239e317a53fe0a81885i0\",\"1c8f2b1f187d04c6fd68cb8d2075739253a0ec8642cf6a21e6dfa89a04c61f45i0\",\"e31369d6146a043c538280f0db13d8301a49269835a78ef5dd1f74fbd3769ffei0\"],\"content_length\":16,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":124,\"id\":\"ac1d4d0cfaeb19d99bc4c6e829d64100b598f2a3cfa538348f230ae3fe09e685i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\",\"number\":1,\"parents\":[\"ee6cabd275c53011248727e064c0be3841799b7391bc337520d9aac1ebdc60d1i0\"],\"previous\":\"ee6cabd275c53011248727e064c0be3841799b7391bc337520d9aac1ebdc60d1i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"e31369d6146a043c538280f0db13d8301a49269835a78ef5dd1f74fbd3769ffe:0:0\",\"timestamp\":1792054033,\"value\":1000}"},"/r/metadata/ac1d4d0cfaeb19d99bc4c6e829d64100b598f2a3cfa538348f230ae3fe09e685i0":{"status":200,"body":"\"a3626f70656269727468646e616d656a77656c6c2d6b6e6f776e646b696e64636f7267\""},"/inscription/4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0":{"status":200,"body":"{\"address\":\"bcrt1ps07srr40g8v2t4e9larkeecct56xj67ue5ead0tuq50lj8888cms3d2xx6\",\"charms\":[\"vindicated\"],\"child_count\":9,\"children\":[\"82d9ba7ae7dd4252fd5bae99d04c70feafcc4f93b0e716e8f8738f914bbea1f6i0\",\"97cd35c5cb52333b987dcea052cd82a7bc9c64a5d2a5fb8c831b42fa63565c8fi0\",\"9414b00451ba60700c236e36c4ead1b86e73d9a76b1e96a46e2f851cc07c6e5fi0\",\"1b48e841f2dbee9cd49a59872ba3ad880d2498c29fed351f54870379982bd10ai0\"],\"content_length\":10,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":124,\"id\":\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"82d9ba7ae7dd4252fd5bae99d04c70feafcc4f93b0e716e8f8738f914bbea1f6i0\",\"number\":2,\"parents\":[\"ee6cabd275c53011248727e064c0be3841799b7391bc337520d9aac1ebdc60d1i0\"],\"previous\":\"ac1d4d0cfaeb19d99bc4c6e829d64100b598f2a3cfa538348f230ae3fe09e685i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"5eaebae86b7957b427121d471de2ff14099467eaa16fded137f7b641ca55788f:0:0\",\"timestamp\":1792054033,\"value\":1000}"},"/r/metadata/4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0":{"status":200,"body":"\"a3626f70656269727468646e616d656474696d65646b696e64636f7267\""},"/inscriptions/block/125/0":{"status":200,"body":"{\"ids\":[\"82d9ba7ae7dd4252fd5bae99d04c70feafcc4f93b0e716e8f8738f914bbea1f6i0\",\"97cd35c5cb52333b987dcea052cd82a7bc9c64a5d2a5fb8c831b42fa63565c8fi0\",\"e29b26c17d48c09e72109ae42a833fb24e000cf89790ebbaebea6d3a884fc1bdi0\",\"9414b00451ba60700c236e36c4ead1b86e73d9a76b1e96a46e2f851cc07c6e5fi0\",\"3d9b514edc1956d5f4fef3edec7267c4920ce81449b8c239e317a53fe0a81885i0\"],\"more\":false,\"page_index\":0}"},"/inscription/82d9ba7ae7dd4252fd5bae99d04c70feafcc4f93b0e716e8f8738f914bbea1f6i0":{"status":200,"body":"{\"address\":\"bcrt1pdg9cqq83ak6mfdmup5qk5a4jrhl7ndu4y5r40vxj6dz5vtgphx2qv5a7y6\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":10,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":125,\"id\":\"82d9ba7ae7dd4252fd5bae99d04c70feafcc4f93b0e716e8f8738f914bbea1f6i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"97cd35c5cb52333b987dcea052cd82a7bc9c64a5d2a5fb8c831b42fa63565c8fi0\",\"number\":3,\"parents\":[\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\"],\"previous\":\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"5411dbe17d62a13595fa3ce8c160d409e58df5d6510859990fb0ea65a71f76fa:0:0\",\"timestamp\":1792054033,\"value\":1000}"},"/r/metadata/82d9ba7ae7dd4252fd5bae99d04c70feafcc4f93b0e716e8f8738f914bbea1f6i0":{"status":200,"body":"\"a3626f70656269727468646e616d65646c756e61646b696e64656167656e74\""},"/inscription/97cd35c5cb52333b987dcea052cd82a7bc9c64a5d2a5fb8c831b42fa63565c8fi0":{"status":200,"body":"{\"address\":\"bcrt1ppsycnfu38tymgtkl45k6fst9wf3jqvwtgv482ahsqtwpl7r5t5sqh7exn4\",\"charms\":[\"vindicated\"],\"child_count\":1,\"children\":[\"53815d63e6f2f437a14d4a0b6691476b75fd9f5efe1a15ec5660fa164f959e0bi0\"],\"content_length\":9,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":125,\"id\":\"97cd35c5cb52333b987dcea052cd82a7bc9c64a5d2a5fb8c831b42fa63565c8fi0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"e29b26c17d48c09e72109ae42a833fb24e000cf89790ebbaebea6d3a884fc1bdi0\",\"number\":4,\"parents\":[\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\"],\"previous\":\"82d9ba7ae7dd4252fd5bae99d04c70feafcc4f93b0e716e8f8738f914bbea1f6i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"53815d63e6f2f437a14d4a0b6691476b75fd9f5efe1a15ec5660fa164f959e0b:0:0\",\"timestamp\":1792054033,\"value\":1000}"},"/r/metadata/97cd35c5cb52333b987dcea052cd82a7bc9c64a5d2a5fb8c831b42fa63565c8fi0":{"status":200,"body":"\"a3626f70656269727468646e616d6563736f6c646b696e64656167656e74\""},"/inscription/e29b26c17d48c09e72109ae42a833fb24e000cf89790ebbaebea6d3a884fc1bdi0":{"status":200,"body":"{\"address\":\"bcrt1pjgfhg4u4sdl55kjwrv0tqu7u0fev4vqcnm8wvuan3r6rvts3dczq25wsdk\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":11,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":125,\"id\":\"e29b26c17d48c09e72109ae42a833fb24e000cf89790ebbaebea6d3a884fc1bdi0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"9414b00451ba60700c236e36c4ead1b86e73d9a76b1e96a46e2f851cc07c6e5fi0\",\"number\":5,\"parents\":[\"ac1d4d0cfaeb19d99bc4c6e829d64100b598f2a3cfa538348f230ae3fe09e685i0\"],\"previous\":\"97cd35c5cb52333b987dcea052cd82a7bc9c64a5d2a5fb8c831b42fa63565c8fi0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"6a091073d4a2de9433f51f6d9e1c19be7c3247dcd3db39f6a092232dd0c84b16:0:0\",\"timestamp\":1792054033,\"value\":1000}"},"/r/metadata/e29b26c17d48c09e72109ae42a833fb24e000cf89790ebbaebea6d3a884fc1bdi0":{"status":200,"body":"\"a3626f70656269727468646e616d65656d61696c72646b696e64636f7267\""},"/inscription/9414b00451ba60700c236e36c4ead1b86e73d9a76b1e96a46e2f851cc07c6e5fi0":{"status":200,"body":"{\"address\":\"bcrt1pe6z87szp5pgsughdxe7pzdv26295r2gjwc2z3e5mujqh7tmw0hqsznhwam\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":11,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":125,\"id\":\"9414b00451ba60700c236e36c4ead1b86e73d9a76b1e96a46e2f851cc07c6e5fi0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"3d9b514edc1956d5f4fef3edec7267c4920ce81449b8c239e317a53fe0a81885i0\",\"number\":6,\"parents\":[\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\"],\"previous\":\"e29b26c17d48c09e72109ae42a833fb24e000cf89790ebbaebea6d3a884fc1bdi0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"7c9dcde4d042e29cb548d44de791fb1e833d9fdc471919c61fb5795cec67c12f:0:0\",\"timestamp\":1792054033,\"value\":1000}"},"/r/metadata/9414b00451ba60700c236e36c4ead1b86e73d9a76b1e96a46e2f851cc07c6e5fi0":{"status":200,"body":"\"a3626f70656269727468646e616d65656173747261646b696e64656167656e74\""},"/inscription/3d9b514edc1956d5f4fef3edec7267c4920ce81449b8c239e317a53fe0a81885i0":{"status":200,"body":"{\"address\":\"bcrt1phesky5yu8uh2slmj3hxy3dh4mmupycmez30lwug4hk8heaaa24hsglg6jk\",\"charms\":[\"vindicated\"],\"child_count\":1,\"children\":[\"6dc11c6cbd657e086d5575e8389212c001835896e20821961fd60ad8043a9d27i0\"],\"content_length\":13,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":125,\"id\":\"3d9b514edc1956d5f4fef3edec7267c4920ce81449b8c239e317a53fe0a81885i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"1b48e841f2dbee9cd49a59872ba3ad880d2498c29fed351f54870379982bd10ai0\",\"number\":7,\"parents\":[\"ac1d4d0cfaeb19d99bc4c6e829d64100b598f2a3cfa538348f230ae3fe09e685i0\"],\"previous\":\"9414b00451ba60700c236e36c4ead1b86e73d9a76b1e96a46e2f851cc07c6e5fi0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"6dc11c6cbd657e086d5575e8389212c001835896e20821961fd60ad8043a9d27:0:0\",\"timestamp\":1792054033,\"value\":1000}"},"/r/metadata/3d9b514edc1956d5f4fef3edec7267c4920ce81449b8c239e317a53fe0a81885i0":{"status":200,"body":"\"a3626f70656269727468646e616d65676c696e65616765646b696e64636f7267\""},"/inscriptions/block/126/0":{"status":200,"body":"{\"ids\":[\"1b48e841f2dbee9cd49a59872ba3ad880d2498c29fed351f54870379982bd10ai0\",\"b111205f0bd02377df8b257519ecef674cd634ddfe5114a0845cd487cd017613i0\",\"1c7688e00eca2a8412c43453633aeeb483996d515cfd53179fc43e5164bd401fi0\",\"9bbc56cfdf2f74e433db01cf469ceff11f2842135bd9d40714c3baff52b44ca2i0\"],\"more\":false,\"page_index\":0}"},"/inscription/1b48e841f2dbee9cd49a59872ba3ad880d2498c29fed351f54870379982bd10ai0":{"status":200,"body":"{\"address\":\"bcrt1py42hu3k6jspu62tq7ym7udf7789et8wk7c8axdh2rqalgangzwzsqj67z7\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":14,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":126,\"id\":\"1b48e841f2dbee9cd49a59872ba3ad880d2498c29fed351f54870379982bd10ai0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"b111205f0bd02377df8b257519ecef674cd634ddfe5114a0845cd487cd017613i0\",\"number\":8,\"parents\":[\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\"],\"previous\":\"3d9b514edc1956d5f4fef3edec7267c4920ce81449b8c239e317a53fe0a81885i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"562a265a8c9fcf17cc14f2d5b12787a608688a5d45267f60081d0f6067213214:0:0\",\"timestamp\":1792054033,\"value\":1000}"},"/r/metadata/1b48e841f2dbee9cd49a59872ba3ad880d2498c29fed351f54870379982bd10ai0":{"status":200,"body":"\"a3626f70656269727468646e616d6568646f6373636f7065646b696e64636f7267\""},"/inscription/b111205f0bd02377df8b257519ecef674cd634ddfe5114a0845cd487cd017613i0":{"status":200,"body":"{\"address\":\"bcrt1pdlrf7lp7svu7lhngep0qfr8yxs82kcac7qd4dqkfv9ux959qcjcqw667q6\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":15,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":126,\"id\":\"b111205f0bd02377df8b257519ecef674cd634ddfe5114a0845cd487cd017613i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"1c7688e00eca2a8412c43453633aeeb483996d515cfd53179fc43e5164bd401fi0\",\"number\":9,\"parents\":[\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\"],\"previous\":\"1b48e841f2dbee9cd49a59872ba3ad880d2498c29fed351f54870379982bd10ai0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"b03fc014f02fec419688837739115613e33afec222762e65f43f695bc137e4ee:0:0\",\"timestamp\":1792054033,\"value\":1000}"},"/r/metadata/b111205f0bd02377df8b257519ecef674cd634ddfe5114a0845cd487cd017613i0":{"status":200,"body":"\"a3626f70656269727468646e616d6569646f6c6c686f757365646b696e64636f7267\""},"/inscription/1c7688e00eca2a8412c43453633aeeb483996d515cfd53179fc43e5164bd401fi0":{"status":200,"body":"{\"address\":\"bcrt1pwfqpuwz7ttde7yw4d90ppysmr4rjxku3eyup3f4gw0kwurynhp6s98qfnz\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":16,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":126,\"id\":\"1c7688e00eca2a8412c43453633aeeb483996d515cfd53179fc43e5164bd401fi0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"9bbc56cfdf2f74e433db01cf469ceff11f2842135bd9d40714c3baff52b44ca2i0\",\"number\":10,\"parents\":[\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\"],\"previous\":\"b111205f0bd02377df8b257519ecef674cd634ddfe5114a0845cd487cd017613i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"15194af91c3b165f131717561e8fae973e134a68cbbb8898106d9878ac6a30c2:0:0\",\"timestamp\":1792054033,\"value\":1000}"},"/r/metadata/1c7688e00eca2a8412c43453633aeeb483996d515cfd53179fc43e5164bd401fi0":{"status":200,"body":"\"a3626f70656269727468646e616d656a646f6f646c656c6f6f70646b696e64636f7267\""},"/inscription/9bbc56cfdf2f74e433db01cf469ceff11f2842135bd9d40714c3baff52b44ca2i0":{"status":200,"body":"{\"address\":\"bcrt1p8lkqjgwtzyazl9xyklw7u4q2r0kn76np6vgyu37ql60x7xlt3prq9z880q\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":17,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":126,\"id\":\"9bbc56cfdf2f74e433db01cf469ceff11f2842135bd9d40714c3baff52b44ca2i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"53815d63e6f2f437a14d4a0b6691476b75fd9f5efe1a15ec5660fa164f959e0bi0\",\"number\":11,\"parents\":[\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\"],\"previous\":\"1c7688e00eca2a8412c43453633aeeb483996d515cfd53179fc43e5164bd401fi0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"2cede876cd86f2228eba0fb298fdd7e29529ae59da7e39bb7d01eced0be817c7:0:0\",\"timestamp\":1792054033,\"value\":1000}"},"/r/metadata/9bbc56cfdf2f74e433db01cf469ceff11f2842135bd9d40714c3baff52b44ca2i0":{"status":200,"body":"\"a3626f70656269727468646e616d656b6865617274686672616d65646b696e64636f7267\""},"/inscriptions/block/127/0":{"status":200,"body":"{\"ids\":[\"53815d63e6f2f437a14d4a0b6691476b75fd9f5efe1a15ec5660fa164f959e0bi0\",\"8261538a5044a24061d2528b12ca9ae4a1f78088b838833f82bf8e0f9c9bdb24i0\",\"5eaebae86b7957b427121d471de2ff14099467eaa16fded137f7b641ca55788fi0\",\"c7d79e81fb104160f3843649d958ab9f56d49df4390fb02b93828021d8dd34dci0\"],\"more\":false,\"page_index\":0}"},"/inscription/53815d63e6f2f437a14d4a0b6691476b75fd9f5efe1a15ec5660fa164f959e0bi0":{"status":200,"body":"{\"address\":\"bcrt1phesky5yu8uh2slmj3hxy3dh4mmupycmez30lwug4hk8heaaa24hsglg6jk\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":15,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":127,\"id\":\"53815d63e6f2f437a14d4a0b6691476b75fd9f5efe1a15ec5660fa164f959e0bi0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"8261538a5044a24061d2528b12ca9ae4a1f78088b838833f82bf8e0f9c9bdb24i0\",\"number\":12,\"parents\":[\"97cd35c5cb52333b987dcea052cd82a7bc9c64a5d2a5fb8c831b42fa63565c8fi0\"],\"previous\":\"9bbc56cfdf2f74e433db01cf469ceff11f2842135bd9d40714c3baff52b44ca2i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"e85b9d173a36e28931bc109ab3ab73fac5035f0dc95fdee9084b9d974a39b552:0:0\",\"timestamp\":1792054034,\"value\":1000}"},"/r/metadata/53815d63e6f2f437a14d4a0b6691476b75fd9f5efe1a15ec5660fa164f959e0bi0":{"status":200,"body":"\"a3626f70646d616b65646e616d656a6c61756e63682d6b6974646e6f7465784154686520706167652c2074686520706f737420616e642074686520636865636b6c6973742065766572792076656e74757265206c61756e6368657320776974682e\""},"/inscription/8261538a5044a24061d2528b12ca9ae4a1f78088b838833f82bf8e0f9c9bdb24i0":{"status":200,"body":"{\"address\":\"bcrt1ps07srr40g8v2t4e9larkeecct56xj67ue5ead0tuq50lj8888cms3d2xx6\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":18,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":127,\"id\":\"8261538a5044a24061d2528b12ca9ae4a1f78088b838833f82bf8e0f9c9bdb24i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"5eaebae86b7957b427121d471de2ff14099467eaa16fded137f7b641ca55788fi0\",\"number\":13,\"parents\":[\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\"],\"previous\":\"53815d63e6f2f437a14d4a0b6691476b75fd9f5efe1a15ec5660fa164f959e0bi0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"8261538a5044a24061d2528b12ca9ae4a1f78088b838833f82bf8e0f9c9bdb24:1:0\",\"timestamp\":1792054034,\"value\":1000}"},"/r/metadata/8261538a5044a24061d2528b12ca9ae4a1f78088b838833f82bf8e0f9c9bdb24i0":{"status":200,"body":"\"a4626f7065617761726462746f784239376364333563356362353233333362393837646365613035326364383261376263396336346135643261356662386338333162343266613633353635633866693065736b696c6c666c61756e63686462656c7465677265656e\""},"/inscription/5eaebae86b7957b427121d471de2ff14099467eaa16fded137f7b641ca55788fi0":{"status":200,"body":"{\"address\":\"bcrt1ps07srr40g8v2t4e9larkeecct56xj67ue5ead0tuq50lj8888cms3d2xx6\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":16,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":127,\"id\":\"5eaebae86b7957b427121d471de2ff14099467eaa16fded137f7b641ca55788fi0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"c7d79e81fb104160f3843649d958ab9f56d49df4390fb02b93828021d8dd34dci0\",\"number\":14,\"parents\":[\"4375d999401ea1996d031bddfc2192170e2f8519ada1ff2a5bc77a5eb3b8a514i0\"],\"previous\":\"8261538a5044a24061d2528b12ca9ae4a1f78088b838833f82bf8e0f9c9bdb24i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"5eaebae86b7957b427121d471de2ff14099467eaa16fded137f7b641ca55788f:1:0\",\"timestamp\":1792054034,\"value\":1000}"},"/r/metadata/5eaebae86b7957b427121d471de2ff14099467eaa16fded137f7b641ca55788fi0":{"status":200,"body":"\"a4626f7065617761726462746f784238326439626137616537646434323532666435626165393964303463373066656166636334663933623065373136653866383733386639313462626561316636693065736b696c6c64706f73746462656c74657768697465\""},"/inscription/c7d79e81fb104160f3843649d958ab9f56d49df4390fb02b93828021d8dd34dci0":{"status":200,"body":"{\"address\":\"bcrt1pmhndmcwyd3qt3lzq7x2fyxa62r8fpj9rn4gr05un6fxt35ft2uzs3tdaf7\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":32,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":127,\"id\":\"c7d79e81fb104160f3843649d958ab9f56d49df4390fb02b93828021d8dd34dci0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"1c8f2b1f187d04c6fd68cb8d2075739253a0ec8642cf6a21e6dfa89a04c61f45i0\",\"number\":15,\"parents\":[\"ee6cabd275c53011248727e064c0be3841799b7391bc337520d9aac1ebdc60d1i0\"],\"previous\":\"5eaebae86b7957b427121d471de2ff14099467eaa16fded137f7b641ca55788fi0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"c7d79e81fb104160f3843649d958ab9f56d49df4390fb02b93828021d8dd34dc:1:0\",\"timestamp\":1792054034,\"value\":1000}"},"/r/metadata/c7d79e81fb104160f3843649d958ab9f56d49df4390fb02b93828021d8dd34dci0":{"status":200,"body":"\"a3626f7066646563696465657469746c65781945766572792076656e747572652067657473206120706167656377687978383c76656e747572653e2e796f6c6f7072656e6575722e6c6f6c2c207365727665642066726f6d206f6e65207061676573206272616e63682e\""},"/inscriptions/block/128/0":{"status":200,"body":"{\"ids\":[\"1c8f2b1f187d04c6fd68cb8d2075739253a0ec8642cf6a21e6dfa89a04c61f45i0\",\"6dc11c6cbd657e086d5575e8389212c001835896e20821961fd60ad8043a9d27i0\"],\"more\":false,\"page_index\":0}"},"/inscription/1c8f2b1f187d04c6fd68cb8d2075739253a0ec8642cf6a21e6dfa89a04c61f45i0":{"status":200,"body":"{\"address\":\"bcrt1p436wl2jp50ceq825lm0em2mtvyafeqvf09ggsa9ku6z9rusqat7q40jzp5\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":8,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":128,\"id\":\"1c8f2b1f187d04c6fd68cb8d2075739253a0ec8642cf6a21e6dfa89a04c61f45i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"6dc11c6cbd657e086d5575e8389212c001835896e20821961fd60ad8043a9d27i0\",\"number\":16,\"parents\":[\"ac1d4d0cfaeb19d99bc4c6e829d64100b598f2a3cfa538348f230ae3fe09e685i0\"],\"previous\":\"c7d79e81fb104160f3843649d958ab9f56d49df4390fb02b93828021d8dd34dci0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"1c8f2b1f187d04c6fd68cb8d2075739253a0ec8642cf6a21e6dfa89a04c61f45:1:0\",\"timestamp\":1792054034,\"value\":1000}"},"/r/metadata/1c8f2b1f187d04c6fd68cb8d2075739253a0ec8642cf6a21e6dfa89a04c61f45i0":{"status":200,"body":"\"a2626f70686772616475617465626f667842336439623531346564633139353664356634666566336564656337323637633439323063653831343439623863323339653331376135336665306138313838356930\""},"/inscription/6dc11c6cbd657e086d5575e8389212c001835896e20821961fd60ad8043a9d27i0":{"status":200,"body":"{\"address\":\"bcrt1phesky5yu8uh2slmj3hxy3dh4mmupycmez30lwug4hk8heaaa24hsglg6jk\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":31,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":128,\"id\":\"6dc11c6cbd657e086d5575e8389212c001835896e20821961fd60ad8043a9d27i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"99e1765b81767a7a65dee3d9594864eb403438f6dafe94d8f75f83978a04b69ci0\",\"number\":17,\"parents\":[\"3d9b514edc1956d5f4fef3edec7267c4920ce81449b8c239e317a53fe0a81885i0\"],\"previous\":\"1c8f2b1f187d04c6fd68cb8d2075739253a0ec8642cf6a21e6dfa89a04c61f45i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"6dc11c6cbd657e086d5575e8389212c001835896e20821961fd60ad8043a9d27:1:0\",\"timestamp\":1792054034,\"value\":1000}"},"/r/metadata/6dc11c6cbd657e086d5575e8389212c001835896e20821961fd60ad8043a9d27i0":{"status":200,"body":"\"a3626f7066646563696465657469746c6578184c696e65616765206765747320697473206f776e206f7267637768797844497473206f776e207265706f206f6e20546561687562206e6f772c206c696e656167652e6c6c63206c617465723a2061646f707465642c206e6f7420646572697665642e\""},"/inscriptions/block/129/0":{"status":200,"body":"{\"ids\":[\"99e1765b81767a7a65dee3d9594864eb403438f6dafe94d8f75f83978a04b69ci0\"],\"more\":false,\"page_index\":0}"},"/inscription/99e1765b81767a7a65dee3d9594864eb403438f6dafe94d8f75f83978a04b69ci0":{"status":200,"body":"{\"address\":\"bcrt1pzxm6rymh0s2az4em6yq7c20vwflevk8auqagru943s4eav3nuy5sccdkw5\",\"charms\":[],\"child_count\":1,\"children\":[\"f7a07a7184500db8091eab383cf54be4fc1577d0ea8c4d504610efab9435e200i0\"],\"content_length\":10,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":129,\"id\":\"99e1765b81767a7a65dee3d9594864eb403438f6dafe94d8f75f83978a04b69ci0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"f7a07a7184500db8091eab383cf54be4fc1577d0ea8c4d504610efab9435e200i0\",\"number\":18,\"parents\":[],\"previous\":\"6dc11c6cbd657e086d5575e8389212c001835896e20821961fd60ad8043a9d27i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"f7a07a7184500db8091eab383cf54be4fc1577d0ea8c4d504610efab9435e200:0:0\",\"timestamp\":1792054035,\"value\":1000}"},"/r/metadata/99e1765b81767a7a65dee3d9594864eb403438f6dafe94d8f75f83978a04b69ci0":{"status":200,"body":"\"a2626f7065666f756e64646e616d656461636d65\""},"/inscriptions/block/130/0":{"status":200,"body":"{\"ids\":[\"f7a07a7184500db8091eab383cf54be4fc1577d0ea8c4d504610efab9435e200i0\"],\"more\":false,\"page_index\":0}"},"/inscription/f7a07a7184500db8091eab383cf54be4fc1577d0ea8c4d504610efab9435e200i0":{"status":200,"body":"{\"address\":\"bcrt1p3r8lsat3rav73tq7ad46jahxguu9gc8rah6ryec9t4e079mqapxq69txm5\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":11,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":130,\"id\":\"f7a07a7184500db8091eab383cf54be4fc1577d0ea8c4d504610efab9435e200i0\",\"metaprotocol\":\"lineage/v0\",\"next\":\"e31369d6146a043c538280f0db13d8301a49269835a78ef5dd1f74fbd3769ffei0\",\"number\":19,\"parents\":[\"99e1765b81767a7a65dee3d9594864eb403438f6dafe94d8f75f83978a04b69ci0\"],\"previous\":\"99e1765b81767a7a65dee3d9594864eb403438f6dafe94d8f75f83978a04b69ci0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"091aee731a6febafd5dfd6df7dc0b4963f2c6538dc4286607378eda7e03296e2:0:0\",\"timestamp\":1792054035,\"value\":1000}"},"/r/metadata/f7a07a7184500db8091eab383cf54be4fc1577d0ea8c4d504610efab9435e200i0":{"status":200,"body":"\"a3626f70656269727468646e616d656573636f7574646b696e64656167656e74\""},"/inscriptions/block/131/0":{"status":200,"body":"{\"ids\":[\"e31369d6146a043c538280f0db13d8301a49269835a78ef5dd1f74fbd3769ffei0\"],\"more\":false,\"page_index\":0}"},"/inscription/e31369d6146a043c538280f0db13d8301a49269835a78ef5dd1f74fbd3769ffei0":{"status":200,"body":"{\"address\":\"bcrt1p436wl2jp50ceq825lm0em2mtvyafeqvf09ggsa9ku6z9rusqat7q40jzp5\",\"charms\":[\"vindicated\"],\"child_count\":0,\"children\":[],\"content_length\":18,\"content_type\":\"text/plain;charset=utf-8\",\"effective_content_type\":\"text/plain;charset=utf-8\",\"fee\":500,\"height\":131,\"id\":\"e31369d6146a043c538280f0db13d8301a49269835a78ef5dd1f74fbd3769ffei0\",\"metaprotocol\":\"lineage/v0\",\"next\":null,\"number\":20,\"parents\":[\"ac1d4d0cfaeb19d99bc4c6e829d64100b598f2a3cfa538348f230ae3fe09e685i0\"],\"previous\":\"f7a07a7184500db8091eab383cf54be4fc1577d0ea8c4d504610efab9435e200i0\",\"properties\":{\"gallery\":[],\"attributes\":{\"title\":null,\"traits\":{}}},\"rune\":null,\"sat\":null,\"satpoint\":\"e31369d6146a043c538280f0db13d8301a49269835a78ef5dd1f74fbd3769ffe:1:0\",\"timestamp\":1792054035,\"value\":1000}"},"/r/metadata/e31369d6146a043c538280f0db13d8301a49269835a78ef5dd1f74fbd3769ffei0":{"status":200,"body":"\"a4626f7065617761726462746f784266376130376137313834353030646238303931656162333833636635346265346663313537376430656138633464353034363130656661623934333565323030693065736b696c6c666c697374656e6462656c74657768697465\""}},"chain":{"network":"super-testnet-local-v0","version":1,"blocks":[{"height":1,"previous":"0000000000000000000000000000000000000000000000000000000000000000","time":1792054032828,"raws":["02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff191873757065722d746573746e65742d6c6f63616c2d76303a31fdffffff0100f2052a0100000022512050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000","020000000001018887fa567742ad750f542abf2afceab31b2a99741a59ff615d8822d491db22c80000000000ffffffff021a09000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b5705f288357700000000225120870491e699efcba9a12b1a23e4b860b22cfddcd9798a9122068a9132f2979e2b01405f5b71886cf2f394d17eb26cd448ee15955578b6ff5c25cce26417c47e7657875c2c67bbd926f7d67f6c7fb9b07a4140cbb3f4d2cb88dfac0283f2d36e682c3000000000","020000000001010e419c598e5c032a6bc2016353072af19ee30c96117fb984b08399231afbd9e50000000000ffffffff02dc050000000000002251205011fe88bc5d68159f705ab4ff3b361e6e93f1f0129ac30a1ba94dd25ef506f34a01000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b57050140c7e10d842956275ffdf48de73a7cf2607aa8c0e9c236095f3c20ab14541d139b897a6a871b942ec42c745d223797826a839587f20671af46bc6259e9357b7d2000000000","020000000001018ac72d997097a7e4743211252354e6eb78e7e1226bd24b0828a2959660562c450000000000ffffffff01e803000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b570503406a97a2a697bd738178b6022460196c9eba88db862216745b312d43123629fdd8474bb19691c4727896390971a932a33bf4b5ad362d6f3f9f0924f1318d16c6bc8220dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b5705ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3801051ba2626f7065666f756e64646e616d656b796f6c6f7072656e65757201070a6c696e656167652f76300011666f756e6420796f6c6f7072656e6575726821c1dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b570500000000","020000000001020e419c598e5c032a6bc2016353072af19ee30c96117fb984b08399231afbd9e50100000000ffffffff5d46694e759fc9c51823b8c8c766ff07ff0f10c190b55c27f141ac7f2db0f6350100000000ffffffff02005ed0b200000000160014e332716e4a3d6673ffc3212ad9153bca597e054ffebc9a3b00000000225120870491e699efcba9a12b1a23e4b860b22cfddcd9798a9122068a9132f2979e2b01408a38308e967a3f17d326bb95d2bba3c87d79467fd51818210364a2e145fd81af536c7e6ace2ca763658445af83986d96f4b66e0a18068fdcafab35876a14264d0140610b58202d91037e54346d35f69ed766e279817d995ab6912891370f451be0c18f64f515c69561498e56be0912107326095cb9bfaadeee69d488cfd3e653573900000000","02000000000103826b71075dd4a40836d4f073983f2bc25bebc53b7a5636afe31ee5ef1eb653c10100000000ffffffffa8e71b6ab40e97f76e84c7f13a0e8a5592eec2ba134ab1f8f820473ab18c2bfc0100000000ffffffff2ac437639f6b20a0df06097f3369004c28e89806a7eadd0493abda47ac8f481d0100000000ffffffff02005ed0b200000000160014e332716e4a3d6673ffc3212ad9153bca597e054f0abb9a3b00000000225120870491e699efcba9a12b1a23e4b860b22cfddcd9798a9122068a9132f2979e2b014021487f9ad7da663e8926facc9930b45e718ee3c3acb86b3e15d349d4f04ea30e84bcaaa0a71c23ad9d03a90a31dea418eff0dd86345a843447cfc645f60887e6014063b24702a8b6e1cf4389135430d084d240068fd58b8beced5e5422c959e3114cbb0cdc8773222f05b7845b1e0cc6ab5111a64ab6a732b50fa34276794c1229b601403a113977b4e3ffc288db348f464f5b69e49e6f6d2f3799792ad45e8bd5be6576682176050205593ccf035f6a22c8d8c9cbd9a64ca73ac5b4ce0203a1beb0d1a500000000"],"hash":"140d9ae6bef13ce4ee7394939c48bcff03c6648a6ee0e6b4add454f40061a0bc"},{"height":2,"previous":"140d9ae6bef13ce4ee7394939c48bcff03c6648a6ee0e6b4add454f40061a0bc","time":1792054032982,"raws":["02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff191873757065722d746573746e65742d6c6f63616c2d76303a32fdffffff0100f2052a0100000022512050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000","020000000001012ac437639f6b20a0df06097f3369004c28e89806a7eadd0493abda47ac8f481d0000000000ffffffff021027000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b5705fc34d0b200000000160014e332716e4a3d6673ffc3212ad9153bca597e054f0247304402201f66a06575798c13a37f1f2e8cc9f2facacd6c6325264f348d3658c3ea06937402201d572eeed037c660002fcbbaff9696802f4096225bcbc8def196d141a1a5718d012102c2525de1344d994d3cd4018bb4bd0a607c5f4a8bafc4404c37dd3c9d31acdc5f00000000","020000000001014673ae237d9746c2e69b4ec2d817f28edab847bb3231638aff740b16a98ea88c0000000000ffffffff02dc050000000000002251209d22157e1a44ced634e4b5b1528fe95a156460abbbd692ae62f330b81297b589401f000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b570501400fc4ccdd03ffd558de915b8c3c608ed8e3cbadeb616522a337990e3f2c54ae69bd86a0f6226b2d47c3a3dcac1e25bdd2cea52ee3b74e1e83c95be08c3f97468a00000000","02000000000102d160dcebc1aad9207533bc91739b794138bec064e02787241130c575d2ab6cee0000000000ffffffff991d549bef0d1780b0f93afddecef206e3fd250c1f7db1b832486e1f55b70b290000000000ffffffff02e803000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b5705e803000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b5705014000bdf644bdb23b9f8529ee4aaf13172b95ea90e4f089e2f7a0fe1f25e641fae773e5126a321f26b97015757e386620ed349af49da718e17bb3838fd27562352c0340edd18bcb9f71af6d7776f17724ae2889a1de106efb92b0e97cec8c36e8fa9fbe994eb34945b64e33de9491eec607ad12a5f001d6b83e19c4268a85b39dd068e6b120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b5705ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e803010320d160dcebc1aad9207533bc91739b794138bec064e02787241130c575d2ab6cee010523a3626f70656269727468646e616d656a77656c6c2d6b6e6f776e646b696e64636f726701070a6c696e656167652f7630001062697274682077656c6c2d6b6e6f776e6821c1dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b570500000000","0200000000010285e609fee30a238f3438a5cfa3f298b50041d629e8c6c49bd919ebfa0c4d1dac0100000000ffffffff991d549bef0d1780b0f93afddecef206e3fd250c1f7db1b832486e1f55b70b290100000000ffffffff02e803000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc4c1d000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b57050140c8767e599dae2118d0075d72ef945ca4de59820a97728d19e22c68d335494ff367c961f885374196b4e40f2f483ee55d31cd27f188160a59e0089b486301e6f70140419f2d5a366fd177a80bd1cca8bdbdb30f5677e9bd12917892c06a3f262bc76fab1bcc5685fa1a37b956e522e7fb2e7cbcd337a4f5e41369df0d08bde7d1759700000000","02000000000101930ba5b360196a85c400b502be698f0b3960f8d8473aa44555effbe26716c2ab0100000000ffffffff02dc05000000000000225120f0672b01621e661ad722dcdef3993ad26370a7745630e4d3f73400af52e2c1d47c15000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b57050140f070f8ecc20f9a7ca80a68c401bbf2d1d726b154a581b87d2e388bf958329eb260973565575b40eca606cfaff3b31ceb8acb25f0cb6ab144af105881478cc54d00000000","0200000000010285e609fee30a238f3438a5cfa3f298b50041d629e8c6c49bd919ebfa0c4d1dac0000000000ffffffff13c484ac01614206fa9a83a13190b7442a6fb4eec7a4b28eab2217b089630fe40000000000ffffffff02e803000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b5705e803000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b57050140a3cc6eed991a5856e4dbbf6a0af476c698869c3b58be81e2d593a897cbd0bfdeeb7ab4af25adb59ab7c95f83646e193d7664062234cfd2bb57ce25c03c303e40034074154466ae6c1736d7197a18f46528516ac31d614b678f9009a787b9fb2c795c497d2aae440e065841b99dde4fbd8066244a7c67cc40dec9ba848c092ea3842ea520dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b5705ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e803010320d160dcebc1aad9207533bc91739b794138bec064e02787241130c575d2ab6cee01051da3626f70656269727468646e616d656474696d65646b696e64636f726701070a6c696e656167652f7630000a62697274682074696d656821c0dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b570500000000","0200000000010214a5b8b35e7ac75b2affa1ad19852f0e179221fcdd1b036d99a11e4099d975430100000000ffffffff13c484ac01614206fa9a83a13190b7442a6fb4eec7a4b28eab2217b089630fe40100000000ffffffff02e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e378813000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b5705014081939b90b9a2630d7ac3974443137457007b98f5a67e647b4cc95424719bdff7fc16ee33f268b0680751f28c60c582b5f9e6bda8746605289dad6990c0973f1f01409013040badae27ed6e5efa20f989c7cec31047d71cbc44d29460a4ceb105158c5c4787c0390bfb3f213613df91fc7efb30cbfc739efb234dc2f3e8728879ff9e00000000"],"hash":"a5d313bf139a010a4264facf84e4a5d46c276f3e18c562b74783dd89e4bc491e"},{"height":3,"previous":"a5d313bf139a010a4264facf84e4a5d46c276f3e18c562b74783dd89e4bc491e","time":1792054033315,"raws":["02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff191873757065722d746573746e65742d6c6f63616c2d76303a33fdffffff0100f2052a0100000022512050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000","020000000001024673ae237d9746c2e69b4ec2d817f28edab847bb3231638aff740b16a98ea88c0100000000ffffffffd9ecf09e9da5ce30d23a10318b6fc8d7ab7be6db5521164382b8ea2c17ff29c70000000000ffffffff0200286bee000000001600147c8042b17ef8708db1f26296f7fcd880a4fc23d50869357700000000160014e332716e4a3d6673ffc3212ad9153bca597e054f0248304502210089d4ba31717ae5a7f0c0e1addf35ede215fd857b082ca533ea7688e81652f88602202effab6e734616f2e543d339cde45e78f81a4a3b3cb3f534915dfc2ba5f6706d012102c2525de1344d994d3cd4018bb4bd0a607c5f4a8bafc4404c37dd3c9d31acdc5f02483045022100f32deb582e5ba7bc28198e045dd082f7f9ef07ae23caf7742f3d8ca18ce38d9a02203e1a8fc9745e0b73bfad9b2da5520a216494c91aee38b73a45f7fbbd40ae854d012102c2525de1344d994d3cd4018bb4bd0a607c5f4a8bafc4404c37dd3c9d31acdc5f00000000","02000000000101cfa0f53ee6bd4928c6566face68842d9fe9129da00f697404b67d225eede41950100000000ffffffff0200ca9a3b00000000160014980717ab75e3c13baa4705e3f57d190edadbd4cd149d9a3b00000000160014e332716e4a3d6673ffc3212ad9153bca597e054f0247304402207b21c3e4daa9557dd3e2da842e29772e83a6850f22e2e2dfec88c39cb6626af6022069cd35f7fb7ad9c09e53c7868021b1373401a39166a038a0c2b13ae0b8c34169012102c2525de1344d994d3cd4018bb4bd0a607c5f4a8bafc4404c37dd3c9d31acdc5f00000000","020000000001013b23324a97c83172bf4ec33c85ed3db4efd951df33e1f7a401bed7d8ce61e16e0000000000ffffffff021027000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafcfca09a3b00000000160014980717ab75e3c13baa4705e3f57d190edadbd4cd02483045022100eb52ce70a94aed4625ad8721610fb7eca678db6c0ef434f459f9729072efd7f202203adb510c88bbbb49100d205cfe532fed778f648b1b26be0a528de64a6f31238f0121020c45291fc31af6310814c9de324132b937bc350b0c5cbfdb922d4f8f1a0aecfc00000000","02000000000101d51f57db334d857eb779b3ce30720da2dc242334c4aee0f34cfb3387c1321a420000000000ffffffff02dc050000000000002251201a5c545114b3e9f6f1e0389033c4bf4d38cca483f43ecb6362dae1fa5797f284401f000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc0140bfc7c19271a79b33206d2bd49f049f9de578dde654e96fef02ff64b3fd0db1310afad2b01d94687ab80f0959f834ff5db7612cef74f3c8ad5f064153213e451400000000","02000000000102930ba5b360196a85c400b502be698f0b3960f8d8473aa44555effbe26716c2ab0000000000ffffffffa33c6f24fa7b43a6cd5251d25e46ce8cf2f12950c6cec5b0ed0ac4cd27a196ef0000000000ffffffff02e803000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafce803000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc014012f54a9728d6161a6f50e92cd18bf9c247dd5a3b07692c93de49ac5800f82a45562e26e8d7b985d836b8501c10ea37c0e7e91c42eac8c6214e760bd7012163b50340a9dd1f58b354184be1b8c4dbe6bdb4907cfea154c373ab5d9b16e766c6cbe19db5dfde35fded663aa17f9adc3f7688e254953137ed0fe1bafc45eb277a2f01aaa720ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafcac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032085e609fee30a238f3438a5cfa3f298b50041d629e8c6c49bd919ebfa0c4d1dac01051ea3626f70656269727468646e616d65656d61696c72646b696e64636f726701070a6c696e656167652f7630000b6269727468206d61696c726821c0ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc00000000","02000000000102bdc14f883a6deaebbaeb9097f80c004eb23f832ae49a10729ec0487dc1269be20100000000ffffffffa33c6f24fa7b43a6cd5251d25e46ce8cf2f12950c6cec5b0ed0ac4cd27a196ef0100000000ffffffff02e8030000000000002251209213745795837f4a5a4e1b1eb073dc7a72cab0189ecee673b388f4362e116e044c1d000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc0140a0e1e3eee8ca2dee41d35a3cd8e67f11cc59685a345ed41de666b950ea443524581292d7507267bc2f7df99bc7ec2a13c4249f15eb7f915ea706347866d2811c0140e5eb5900788d04b746295069a026b978cd955f243996f4b33d1a39a63bcf8a9ce4ce646dde1487b012d9ea203e0bcd269c789ad3df4c31b9dc47a50272d6e08a00000000","02000000000101164bc8d02d2392a0f639dbd3dc47327cbe191c9e6d1ff53394dea2d47310096a0100000000ffffffff02dc05000000000000225120f97a7d62759ad6acacb34a8c40a19b34e3018a4eddf8a7837e0ae2f7a0cfa37a7c15000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc01401a9aa3e66d45dd8f7400622bb95b47fca1a69bd3242539a4c393ec1b391b3fe6d847c042004fd0f1f011307bd5e4cf9dbf7cc759e629a7f7f713444c428b626b00000000","02000000000102bdc14f883a6deaebbaeb9097f80c004eb23f832ae49a10729ec0487dc1269be20000000000ffffffff2e513265b06cbbfc64fcacf638b5b2ed949c481a3041e247ef515e1c4dace9740000000000ffffffff02e803000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafce803000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc01401fb15d1a9351210fbe2a7e32349869d32224435269e01f25f5eeded8cf7a6ec066e44afc4880d02c689b47959dfbe57cb8f88275c9065219cd2fe2b1c41fb14403401b309327b809c4951b862dd847c5918e449abcd11e541728ea13a24bbb6be1b78edb005ab0d08c1f32b4917ee6ff74835d62be352c68fe7f451a5f850564775dab20ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafcac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032085e609fee30a238f3438a5cfa3f298b50041d629e8c6c49bd919ebfa0c4d1dac010520a3626f70656269727468646e616d65676c696e65616765646b696e64636f726701070a6c696e656167652f7630000d6269727468206c696e656167656821c0ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc00000000","020000000001028518a8e03fa517e339c2b84914e80c92c46772ecedf3fef4d55619dc4e519b3d0100000000ffffffff2e513265b06cbbfc64fcacf638b5b2ed949c481a3041e247ef515e1c4dace9740100000000ffffffff02e803000000000000225120a8871f724582acd426388c1b04a459e7d3d27cc75f99f2df4bd6e9f1045700aa8813000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc014071df32f3efce03a5f6f127b6ec32c36caff329f1a21a0e410f64704b0c974d8573c88d7dc32641660a95bcb0b8fab5d8fdb04abfebdbb1c1160aa1165c71644501404e0b4fca24d13a6c7bbb126bb7dae98296043af5e97a9bcecc7b8db2316ba3f3a837c8f028d3662ca228c74fc639cc33713afa16b31a790c67a3a9cbc4252fc200000000","02000000000101cfa0f53ee6bd4928c6566face68842d9fe9129da00f697404b67d225eede41950000000000ffffffff02102700000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37fcfe6aee000000001600147c8042b17ef8708db1f26296f7fcd880a4fc23d50247304402201a7891b484fb306a056991f95739ca985d69304116d6e260c82e1da62b31af9902205dc9a9249323748d3527b5e9ca6e170613b71762756a6d5e9fd7658d0323e0fb012102d7d70c988a52e9b6fefa6b7324f9886a85fbb86b5c63b3d51a08622ccec1dcf300000000","020000000001017cdd36cabb61840da257cb81c55997b75abe2baf2f4e1e97036715e97301af060000000000ffffffff02dc0500000000000022512044287689dae6f21416b1c7d83047821d0862e8404aaec739885bb782cb9cdd7d401f00000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37014014024244e703596c4e51d6c88a090070ee4ca0c6cc4b330f48ae7b019611c20f79597e8868f31ca7e558f767e9ac566a2fd60192ca378ecfe0d1a06200868d0600000000","02000000000102ac81b960e4d4fe921f43231562f9d50dad976bdf1f19d7e64356cd3f81f850ed0000000000ffffffff8ad3184639b4944fbe517fc58deb696769566ee2bb1353d556df6919f59e60810000000000ffffffff02e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140620d68e86dbbc799425c530d578fcfbae01d35436301e60308eac2faf6522642708ce3ad4c58be943cb831b09b1af1d778c9c188a4e0a17a2b7c1ed64356edc003403e8b9ec990923a5113753ed6ee78b71db34bbfa04371ed8c0c545805661453cf388e5e465f818f30491ffdc4eca027317c3911ae7973889ba10ce4a99c028d6aa72083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032014a5b8b35e7ac75b2affa1ad19852f0e179221fcdd1b036d99a11e4099d9754301051fa3626f70656269727468646e616d65646c756e61646b696e64656167656e7401070a6c696e656167652f7630000a6269727468206c756e616821c183fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3700000000","02000000000102f6a1be4b918f73f8e816e7b0934fccaffe704cd099ae5bfd5242dde77abad9820100000000ffffffff8ad3184639b4944fbe517fc58deb696769566ee2bb1353d556df6919f59e60810100000000ffffffff02e8030000000000002251206a0b8000f1edb5b4b77c0d016a76b21dffe9b795250757b0d2d345462d01b9944c1d00000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3701402c497de7640a9bbc5347a12a1e4d0f924ccc4b3cc93cba6a494ac2cf8e6880441fea1354223d0f3c826fc947426d70b02732de9c64ce58102edef7dda2fb5cbf0140072f37285a6b46bfa2280e6dc64d8086bf1239ef69522e50f9eaa61c17cef15fdb84902f6ffc4d07b9317c6d908608cb0e2a2ec72f41e273d32d8e581351ae3600000000","02000000000101fa761fa765eab00f99590851d6f58de509d460c1e83cfa9535a1627de1db11540100000000ffffffff02dc050000000000002251200b4cd9be70e977db5056e9f6eeeefbfa5fcf5dc0d52fed57eaa1ce18a2733ed87c1500000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3701402f9ec6dddcca0b4d1078f7b27283b809179a8f39237d8741d6354208fee3fe49dae112a842e03d4ba9e6303f823f042717bd06797ae12b86e5483640e4d1119d00000000","02000000000102f6a1be4b918f73f8e816e7b0934fccaffe704cd099ae5bfd5242dde77abad9820000000000ffffffff7f0856b1c7f2fa90d6497fccb8350a4ea861367ecf9708e07fa6198b0f944a6a0000000000ffffffff02e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3701406248799698a13b20b39dce65a32c98cf187e92b991caff881a9d49757c8575a2ec01643572b95e1e562e5ae24aaab2941e84226c00997b4334b1f310e85f9d2f034011c2bcc6fb6b15114f9a69c285485cbb076306dc29039697d2801202c8ef702feb3034e9a4c1e030d147a4dd8b3fc229fdb0e69f8f2330d70fe388329eb0c05aa52083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032014a5b8b35e7ac75b2affa1ad19852f0e179221fcdd1b036d99a11e4099d9754301051ea3626f70656269727468646e616d6563736f6c646b696e64656167656e7401070a6c696e656167652f76300009626972746820736f6c6821c083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3700000000","020000000001028f5c5663fa421b838cfba5d2a5649cbca782cd52a0ce7d983b3352cbc535cd970100000000ffffffff7f0856b1c7f2fa90d6497fccb8350a4ea861367ecf9708e07fa6198b0f944a6a0100000000ffffffff02e8030000000000002251200c0989a7913ac9b42edfad2da4c16572632031cb432a7576f002dc1ff8745d20881300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140c237f970305b2809e34b9427b653d5d48d111bc46eeb755ca888b1bb7a5adadf64603de8d0ed6badaf95a43528d454892f9e3b4817a40402a262c0e9ee1583b00140352569184592427e2c892b55cda2cb00162388b5d1b136fa448d81268625979dbe54fe1b3b11be0b2faddb4330dedb25eec7087c787f387bbd258f198345346400000000","02000000000101b62534745dc908f1faf35a78f4ecd9b4080a3a2c5410cb4e6fa30fa2237203a90100000000ffffffff02dc05000000000000225120a7b88a83b4c76ef8f21398481fbf2242d4a819a6c2db63426e8a894c72c0800eb80b00000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140b72a261650d033a24ce6d4d40a56164f8ee3139a1cfea3975a1cb5b8342880e0393c24a20984c889a86d245820779d52d5d56ad440ce8bbdf011eb315f6ba2b000000000","020000000001028f5c5663fa421b838cfba5d2a5649cbca782cd52a0ce7d983b3352cbc535cd970000000000ffffffff405f39bbe7812038ba2b80d7874877c8e108211a4292aabe5de085edbf5a6c6c0000000000ffffffff02e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140f955408ca713455d5b05939a5449d10838a900361e1401437f6f06e5d18002dbeb25471b667c64069020a1e6dbb7ea7df6a7ccd2555f614336c8d4eecfbf3b7b03403d33fd8eb1f9188a439498b11746be4a0f6a15f5eb2e5b25a5c8358d3ecced673bbf6663a872398b136842e5aa309ffbe7932fa0b8e3565b90ca628f36fa2bada92083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032014a5b8b35e7ac75b2affa1ad19852f0e179221fcdd1b036d99a11e4099d97543010520a3626f70656269727468646e616d65656173747261646b696e64656167656e7401070a6c696e656167652f7630000b62697274682061737472616821c083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3700000000","020000000001025f6e7cc01c852f6ea4961e6ba7d9736eb8d1eac4366e230c7060ba5104b014940100000000ffffffff405f39bbe7812038ba2b80d7874877c8e108211a4292aabe5de085edbf5a6c6c0100000000ffffffff02e803000000000000225120ce847f4041a0510e22ed367c11358ad28b41a912761428e69be4817f2f6e7dc1c40900000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140eccf18fb0f1c234abcaf4d4fc4b41d95f1edfcba442e869f6f6b361d33b2b2f9e8fc3b138e25809e9aca00611f355071ba03828787dd4f041f49384885380faa01402557e2be1ee19916502daae52cb19b60882b880d0f9a1be8ca24c7d583b394ce1948ed834ed9418654b473e388e94a715863ea7e9cb4c03323532ca61b8ce92400000000"],"hash":"e94cf39869fc2c85141be53b76549aac40a47615c47107d489954717da0cd967"},{"height":4,"previous":"e94cf39869fc2c85141be53b76549aac40a47615c47107d489954717da0cd967","time":1792054033798,"raws":["02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff191873757065722d746573746e65742d6c6f63616c2d76303a34fdffffff0100f2052a0100000022512050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000","02000000000101d51f57db334d857eb779b3ce30720da2dc242334c4aee0f34cfb3387c1321a420100000000ffffffff0200c2eb0b00000000160014d16a363ff5570f8d8c67884c95ccdf2aa75b1e7808ddae2f00000000160014980717ab75e3c13baa4705e3f57d190edadbd4cd0247304402202e1b545351bdc58fc776eaab679291c5422e6dc4e129855a19357ccec6c97926022005d99576649ab391e39e8436061a5af0c955256d00a730400765b60a08ce0d510121020c45291fc31af6310814c9de324132b937bc350b0c5cbfdb922d4f8f1a0aecfc00000000","020000000001017cdd36cabb61840da257cb81c55997b75abe2baf2f4e1e97036715e97301af060100000000ffffffff02102700000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37f8d56aee000000001600147c8042b17ef8708db1f26296f7fcd880a4fc23d502473044022049c4ab52b3281fafdc63daf1739bf6a3e584b3969f8ae5d5e8acef3786a8e18002207918e197f570a31103aac4c85ad7e2ab3e9c44040e55dd44f47539eb0a6453d6012102d7d70c988a52e9b6fefa6b7324f9886a85fbb86b5c63b3d51a08622ccec1dcf300000000","020000000001012fc167ec5c79b51fc6191947dc9f3d831efb91e74dd448b59ce242d0e4cd9d7c0100000000ffffffff02dc0500000000000022512009f873d7ef0a4a3e13b6f16677494a0a420226d29710a82772b0c8a2f26c9d4ff40100000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3701402f9ddc254fe76ea1ec0e4e39d7ff8f4b5c3872ca4ed7a3b7507b0b6d91e0723cdecc2f48ce1a866f6479336be507e46a2f6bbb0b2ecf6a3a19ca719274a2374000000000","020000000001025f6e7cc01c852f6ea4961e6ba7d9736eb8d1eac4366e230c7060ba5104b014940000000000ffffffff25a60f645b7db44771dbc25ab40c57e9ae473a1c095b566c833bd39559a08be80000000000ffffffff02e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140b0b681269362043b33c7a98bf06bb0fb15723fb95139edb94a14f4652b05c420335c91c15ec8bfbd71cbd48e64a821a78bb44509d71a0d1a14e774acf8557caf0340f7998c33f0ddbf5c2c3065b16679ab272e9544a61c2acc12c23fd497145ad2b7667da4f61c93ff337f85c37516ee2723326b3de7c563ee6c218b6bcbb738e6c5ad2083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032014a5b8b35e7ac75b2affa1ad19852f0e179221fcdd1b036d99a11e4099d97543010521a3626f70656269727468646e616d6568646f6373636f7065646b696e64636f726701070a6c696e656167652f7630000e626972746820646f6373636f70656821c083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3700000000","020000000001020ad12b98790387541f35ed9fc298240d88ada32b87599ad49ceedbf241e8481b0100000000ffffffffe9776bc0f83bf56dd306434c969d46179066ba833a5d1a134a1b0c1d98c4d7560000000000ffffffff02e80300000000000022512025557e46da9403cd2960f137ee353ef1cb959dd6f60fd336ea183bf4766813851c2500000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140ed5493af0bdacfc99d0c53738e123e11c908745a74c88171f486aecbf94f578c3ba30f270f6277cedc0fef8f299f965da123fe579298260f6c646dec745929400140a1eb59be54e125cd0c7164440b2c72393a88c10ab6b2b37a23843466ca51c725a412eb4877acb6cf532c57902f2700189f74b1c7d8af856487f263d28c553ba300000000","0200000000010114322167600f1d08607f26455d8a6808a68727b1d5f214cc17cf9f8c5a262a560100000000ffffffff02dc05000000000000225120a96c1b5a8afffcb9bc728c043ef7453a5b02a5c6ccb479cf1df65cc22fa10d784c1d00000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140e18f3d8a0f18b6c510c5dc47f40303d813e5da1fcd811e8ef699c36985fd71abc0df5bca6603e9e67b56426251a24da85da28812c26adb4cf937f416b6d44bf900000000","020000000001020ad12b98790387541f35ed9fc298240d88ada32b87599ad49ceedbf241e8481b0000000000ffffffff39d52edc38bc9d1dca081ca0db878a6e71c295108af84d0a925c2141980030840000000000ffffffff02e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140cd74516f8b89ff092fe704ab1f84c8f51ef3945250b511d7b9d63799271d1c66cb053f078a50ea5dfbb8e024b71dd9cc4e26e26baac5966eb2ea0cc6d3d1ff0803406554111ec4f53250e71df87219890e5635826eabed5b4de5c35954aa6e1ac628044665f10a8bc2dc3fbab82534f52e0b1614148e3c56dd75ecf59c80b4a90687af2083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032014a5b8b35e7ac75b2affa1ad19852f0e179221fcdd1b036d99a11e4099d97543010522a3626f70656269727468646e616d6569646f6c6c686f757365646b696e64636f726701070a6c696e656167652f7630000f626972746820646f6c6c686f7573656821c083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3700000000","02000000000102137601cd87d45c84a01451fedd34d64c67efec1975258bdf7723d00b5f2011b10100000000ffffffff39d52edc38bc9d1dca081ca0db878a6e71c295108af84d0a925c2141980030840100000000ffffffff02e8030000000000002251206fc69f7c3e8339efde68c85e048ce4340eab63b8f01b5682c9617862d0a0c4b0581b00000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140041685c47a01bf07f3b81e140bcfc1dd42f5cc4a4566224b0ec3cf62031d86a29a9379f9d1f35d25115b9cf890464b800380e2519da87402655e310e626359fb0140b659bc0b027a7b70bc89d48a8898e901f5e90721cf8c5b546cd2b44851fd88584de89fde537710ca3ac6d1243de610f562a2b7e42832018c02da922af2b1c8bc00000000","02000000000101eee437c15b693ff4652e7622c2fe3ae3135611397783889641ec2ff014c03fb00100000000ffffffff02dc05000000000000225120e6ebba7e7c093bd68484a41b57acd6da8fc4573c4bdc5a20044192e0facd2723881300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37014072fe5469b1ca1a91031f1c3beedfd2b46ec912a79ecd3d3bc91f0dbab2a7ec7bb7a9b55904bce38d631de5c534c41faa9b6f1e1ee7dfd61111946c123e73edca00000000","02000000000102137601cd87d45c84a01451fedd34d64c67efec1975258bdf7723d00b5f2011b10000000000fffffffff724917513655abace6d34a99476a6d5681dce00ca5ef24c80d6d2bab833b1470000000000ffffffff02e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140dba93bd290f5d9d63a62f5e3336d0ed710e8c427555d8f7ed1663dee0e5dd0fbb17019d8ed23113f364889f24adca765dff999ba7f33e6d82b3e118f0e13419c03400ebeb40cac0347faf79955de72c224b36af5596009f6274d29fae589b553c77ddbf9c5c5bd44d0fd2fe4d8a61ef67b6668ff518b5afd380e8d8ff1011fcc49c4b12083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032014a5b8b35e7ac75b2affa1ad19852f0e179221fcdd1b036d99a11e4099d97543010523a3626f70656269727468646e616d656a646f6f646c656c6f6f70646b696e64636f726701070a6c696e656167652f76300010626972746820646f6f646c656c6f6f706821c083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3700000000","020000000001021f40bd64513ec49f1753fd5c516d9983b4ee3a635334c412842aca0ee088761c0100000000fffffffff724917513655abace6d34a99476a6d5681dce00ca5ef24c80d6d2bab833b1470100000000ffffffff02e80300000000000022512072401e385e5adb9f11d5695e10921b1d47235b91c93818a6a873ecee0c93b875941100000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3701406852098be7beb177b49d73eeb91259981f68e6ee9966ab0999af3d171bfb1ec53d7be434b90f11990c478f077762b7dbe9def00d850d9c6c46f217c8cecd1b420140b9b3ad45c561d8eeea4b0ad070e65b6b795b33c2c036b3c8e101e2caf8c5deedb90cd11789147d1a7713e13ad312920308f3e81e2c8e2accb9db234fccc4435700000000","02000000000101c2306aac78986d109888bbcb684a133e97ae8f1e561717135f163b1cf94a19150100000000ffffffff02dc0500000000000022512075e76c5700b76f10d3ce2604cb72a5d2c48390a7e4889d5f6693b0b7e1652b07c40900000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140520cd1cecc48074d8782322b1e37d9b7494bdeb6d4b1ec7d5108ea6eac492ff5bf48a9f7e8eda683cd1992441ca7118a687ae9cdf36fe2bde6231f32b2593b5600000000","020000000001021f40bd64513ec49f1753fd5c516d9983b4ee3a635334c412842aca0ee088761c0000000000ffffffffd80c82b33120db320f6cbfe505186c589410f058afd6a5da053fa2c9e515dbac0000000000ffffffff02e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140bb4fcca40d218a60be6a46dbdf03d51a734ff372b9e2e30bc898b3570b72f5c9c6b8f0f016a1e78279e3a946681f3f1ab4271eec4aa37582649b18f76532fd7e03405ba2a37f038dff843efff1ee68006c800f14ad025dbbf9fbd418a622ab32cc7f84f2fc07621995186d6347e4bd4a1c8fd262a30e3a1fe06fdebbb8439da94ef7b32083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032014a5b8b35e7ac75b2affa1ad19852f0e179221fcdd1b036d99a11e4099d97543010524a3626f70656269727468646e616d656b6865617274686672616d65646b696e64636f726701070a6c696e656167652f763000116269727468206865617274686672616d656821c083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3700000000","02000000000102a24cb452ffbac31407d4d95b1342281ff1ef9c46cf01db33e4742fdfcf56bc9b0100000000ffffffffd80c82b33120db320f6cbfe505186c589410f058afd6a5da053fa2c9e515dbac0100000000ffffffff02e8030000000000002251203fec0921cb113a2f94c4b7ddee540a1bed3f6a61d3104e47c0fe9e6f1beb8846d00700000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140cfbdf8ea0f08a3737eba052e14146a9bc7adfde4652a7ff2fc9c078e157a95264927c9fd7a18cc2fc85e375a333ee56c335e6b090ce2798897f68403ed1439ab01407c84e19548a9a109f60e12047a179019c8df05f61ccf80a3fdea5139ca2eacce08d8fa81464bdb2190f6278275a4158cfa14ec970c5e9e52a07c7a3cd7dafe2c00000000"],"hash":"daa43da30fe58f95b66ccb99bf9665ef7cd8f69bb2180c73e30873a91cb8037c"},{"height":5,"previous":"daa43da30fe58f95b66ccb99bf9665ef7cd8f69bb2180c73e30873a91cb8037c","time":1792054034239,"raws":["02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff191873757065722d746573746e65742d6c6f63616c2d76303a35fdffffff0100f2052a0100000022512050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000","02000000000101e9776bc0f83bf56dd306434c969d46179066ba833a5d1a134a1b0c1d98c4d7560100000000ffffffff0200e1f50500000000160014b9af7e8f72352b0b03a1f5c37a677320c03fb53e04f374e8000000001600147c8042b17ef8708db1f26296f7fcd880a4fc23d50247304402202875ad45d028f1ae64995a24620a8854ef11d3ed2318c192dd28369166dd5bbf02205e8f978515f69bde009870651fcd62e8f126a1459d2ee6dc0b97e0895a8284f1012102d7d70c988a52e9b6fefa6b7324f9886a85fbb86b5c63b3d51a08622ccec1dcf300000000","0200000000010181def83387058fb8b96c0c4caf6e361a6631709d2e00c41066072e955c6e3a8f0100000000ffffffff0280f0fa02000000001600145d8b0906e10a3a9578d2247ddc00f544b78533f590007ae5000000001600147c8042b17ef8708db1f26296f7fcd880a4fc23d502483045022100ce78a8bda195eb89a827c4b45d3a36e1a0cd157dedca2c4b4700fa192410c8040220391d33e4c8c1e4c8e05fa85b9de58b7c5c3264186fd3058c3c66569b0d5f7b37012102d7d70c988a52e9b6fefa6b7324f9886a85fbb86b5c63b3d51a08622ccec1dcf300000000","020000000001014552d7b16b03dbaaef97a721213ca100bd56152063330f06f7c6e66d0e8f0abd0100000000ffffffff02102700000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e378cd779e5000000001600147c8042b17ef8708db1f26296f7fcd880a4fc23d502473044022035b8956fc02d71442be0764e41bc54c35e371b1e14132b546cd50a272701d314022015f6bf1aee65ee50cc191ba6e5bff5670dcdd821c052ddefca99eed0f8d20e9f012102d7d70c988a52e9b6fefa6b7324f9886a85fbb86b5c63b3d51a08622ccec1dcf300000000","02000000000101ef160a3ed624afa94f3a8b8acb787660cd8e320f125879b6e5ec83f3177b6b100000000000ffffffff02dc05000000000000225120ba944843ae2d06d1bdfabff64da6abefca585452cac7b979eacd6e35e42c13fd401f00000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140c0a6b1f2ece4f23ce39f33c7dd4580715a45708437cafbb54ddec4e918026acdc5c5dbe9c5a3820335cc411ffd946c200a1980ae97b52a17eb9c50398813324500000000","02000000000102a24cb452ffbac31407d4d95b1342281ff1ef9c46cf01db33e4742fdfcf56bc9b0000000000ffffffffc93d38f148fb9bb621cca4bd9e130a88376731aa0f92bfb4a287d1e7aafbf2cb0000000000ffffffff02e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140b8c5d67a8ce4c03aa9b9c72c1376c180f2cd7c9367c4df0132e48cc4cddf3b1e1e8d8807053f65cc2a8bcb8c1359073f991c227fa53f918f55001a71ce14be3803403bdf8cbc91012cddf5599608de1fa07cb21a6e40d00960d1b6996b512b45ad52e2d1d8392cfd5ecd5c1f977916f8b47618f5332ae805b90bffad35fca3794478fa2083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032014a5b8b35e7ac75b2affa1ad19852f0e179221fcdd1b036d99a11e4099d9754301054c69a4626f7065617761726462746f784239376364333563356362353233333362393837646365613035326364383261376263396336346135643261356662386338333162343266613633353635633866693065736b696c6c666c61756e63686462656c7465677265656e01070a6c696e656167652f763000126177617264206c61756e636820677265656e6821c083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3700000000","02000000000101c93d38f148fb9bb621cca4bd9e130a88376731aa0f92bfb4a287d1e7aafbf2cb0100000000ffffffff02dc0500000000000022512053535be282e927879e48fd65ccb2364d50324df49e88ad546feb6dd8c8224f13701700000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37014068dbccdfc268262c7cbb9e19c0e2d16863d8e12510b14071fc4f0e86d5e0ef62b446d4dd68b87380f62774907ea2eced47df87ced9e2d75e32f64f46abf0d32800000000","0200000000010224db9b9c0f8ebf823f8338b88880f7a1e49aca128b52d26140a244508a5361820000000000ffffffffae214f816d1acdd56e1cda2b254867ad5e606a166b0d95f90ece49efb8de62820000000000ffffffff02e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37e80300000000000022512083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e370140012c964c4706e38e700c24f817db7a757d9accf4bdf48ad66dab1cf2fc4d8cb5639ed650ae1edf8edb013e77846ca16866777921b40604bdfdade2cafafc0b3103409192c37431a49e4c0e698bf3e5362218f362a9d0057f6b281f29db691a1bf3b32a6c3068691f588d8b2edc9d51448e49b844c42aad0ac85f5ef3128b83518553f62083fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e37ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032014a5b8b35e7ac75b2affa1ad19852f0e179221fcdd1b036d99a11e4099d9754301054c67a4626f7065617761726462746f784238326439626137616537646434323532666435626165393964303463373066656166636334663933623065373136653866383733386639313462626561316636693065736b696c6c64706f73746462656c7465776869746501070a6c696e656167652f76300010617761726420706f73742077686974656821c183fd018eaf41d8a5d725ff476ce7185d34696bdccd33d6bd7c051ff91ce73e3700000000","02000000000101ac81b960e4d4fe921f43231562f9d50dad976bdf1f19d7e64356cd3f81f850ed0100000000ffffffff02dc050000000000002251208af3e72d849a762f9e33ed1675fea4d0a1d191d813d62d1b6cdd89a488b9f4e2b80b000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b570501400bcf63801d76f1dbee3b7c7ce4771905dc78a8c02b0957124613d82f36fbb9fb298b0565d870e880a8bd32ef55711bb0f379909c28092ab776a304d16725df0e00000000","0200000000010214a5b8b35e7ac75b2affa1ad19852f0e179221fcdd1b036d99a11e4099d975430000000000ffffffff005b1e55185623624f71ba351cbb1593fa963246950b9318b1ff9a522ab96c9a0000000000ffffffff02e803000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b5705e803000000000000225120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b57050140e6e69a09e02562e0dbd4d1921d028c754be13da53ec4fa3f1523e7de989a532793ed3212edef99f30f1b556b799dbd9e541370d6a3e36f4a54265daf2a95ec730340752c79494c793ef31adb7c52fc845bca7faad8f326d4b6ec0e10971448de81354b564fe8aebbba7a06437181bc49016b253216665d0b57bdd3fb0c152bdc4dcffd090120dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b5705ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e803010320d160dcebc1aad9207533bc91739b794138bec064e02787241130c575d2ab6cee01054c6aa3626f7066646563696465657469746c65781945766572792076656e747572652067657473206120706167656377687978383c76656e747572653e2e796f6c6f7072656e6575722e6c6f6c2c207365727665642066726f6d206f6e65207061676573206272616e63682e01070a6c696e656167652f763000206465636964652045766572792076656e747572652067657473206120706167656821c1dde6dde1c46c40b8fc40f194921bba50ce90c8a39d5037d393d24cb8d12b570500000000","0200000000010181def83387058fb8b96c0c4caf6e361a6631709d2e00c41066072e955c6e3a8f0000000000ffffffff0210270000000000002251200c0989a7913ac9b42edfad2da4c16572632031cb432a7576f002dc1ff8745d20fcb7f50500000000160014b9af7e8f72352b0b03a1f5c37a677320c03fb53e0247304402204c32684e12e69fab1bf6b4faa4f1e7111e40ebbded083259357360e4702d258402205f57e86b7f99d0b9683e89b10cb0f7688b61fb7db85a28729340751e0b915776012103d76a1c80a7b2b1c09a9ac4ad38cfb5183bb9b100b0adde5cb4ab12afbb65f64900000000","0200000000010160c7d55bcf37e46ddac9ce5e60b6a6db8ad4a55b45c8f4594dafa9796162f5e80000000000ffffffff02dc0500000000000022512074d287b3a0eab6411e6b4933754cd584ab5a08e3c3db79eae59affd45f7f7a7f401f0000000000002251200c0989a7913ac9b42edfad2da4c16572632031cb432a7576f002dc1ff8745d2001404974cef88dbe8b3dc3be92aa0cdec0d7a4bcf8ac50549a7f492de173ef58dd8df01ea9ea32256158e2432a8c59261f48503671c8e26719e17e64d36defa136b600000000","02000000000102b62534745dc908f1faf35a78f4ecd9b4080a3a2c5410cb4e6fa30fa2237203a90000000000ffffffff68dc75cb603321f63257b850cc43b08d943b23f954c5dffa5961c7010131ca170000000000ffffffff02e8030000000000002251200c0989a7913ac9b42edfad2da4c16572632031cb432a7576f002dc1ff8745d20e8030000000000002251200c0989a7913ac9b42edfad2da4c16572632031cb432a7576f002dc1ff8745d2001402523c8532e826c4a024a8e8a342c6921e357713d6283301501647ec5238c593828b9a9335ead8ae6e697b82e6ac6a82c14ee611a1ec1e85078ce7409cd8c5634034098794a686b39956afc01a03fbe9c70d9c4d90ea22d750cbd62e2d06861b9e2077feb69ee33c7e50438375c9b628e482ec0674656c7b5203bb61a4d703221bc2def200c0989a7913ac9b42edfad2da4c16572632031cb432a7576f002dc1ff8745d20ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e8030103208f5c5663fa421b838cfba5d2a5649cbca782cd52a0ce7d983b3352cbc535cd9701054c61a3626f70646d616b65646e616d656a6c61756e63682d6b6974646e6f7465784154686520706167652c2074686520706f737420616e642074686520636865636b6c6973742065766572792076656e74757265206c61756e6368657320776974682e01070a6c696e656167652f7630000f6d616b65206c61756e63682d6b69746821c00c0989a7913ac9b42edfad2da4c16572632031cb432a7576f002dc1ff8745d2000000000"],"hash":"8850997fccd7ba7185bb15ee8892452038efe10d0df13042e9002447099f3306"},{"height":6,"previous":"8850997fccd7ba7185bb15ee8892452038efe10d0df13042e9002447099f3306","time":1792054034665,"raws":["02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff191873757065722d746573746e65742d6c6f63616c2d76303a36fdffffff0100f2052a0100000022512050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000","02000000000101cd8d7ba39ab8e27c3c2da97a373024982a1fcfc797981410d32787a8f803020e0100000000ffffffff02dc0500000000000022512067d2fffa98a64c924d71420ef5bd9de3a0acd9903fa58c8183554d9b400ccd7ab80b000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc0140ed7c5e7752073da716647c09f6b68a414cac1eeaf800ad3385f4067a85ce9ca8c1b100935007651a435eafda61de43b4fb883ff156714b9ec8ffffb4857419f200000000","020000000001028518a8e03fa517e339c2b84914e80c92c46772ecedf3fef4d55619dc4e519b3d0000000000ffffffffd4fbe90c215faa155a298f04ddbc91076b818ca102a437975e807b3f68d5de900000000000ffffffff02e803000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafce803000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc01409cf4fdcde079134b426575e1aaf9669ce71cd0d1dc6998999c34beb6bdb1c750b215084a0db3e6fb835ab9d31a15b46ba2f7497ad80fd89b83eacb4486d29f2f0340a9fbe6574651663c938d7eeb1cdd68ac1a33b3b7e042f425ac90d3ad07af8fdcae3ab29a8727ce5c22292586d95cf02126720ec14761f7b498301621e61fe713db20ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafcac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032085e609fee30a238f3438a5cfa3f298b50041d629e8c6c49bd919ebfa0c4d1dac01054c54a2626f70686772616475617465626f66784233643962353134656463313935366435663466656633656465633732363763343932306365383134343962386332333965333137613533666530613831383835693001070a6c696e656167652f7630000867726164756174656821c1ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc00000000","020000000001015ac0123a08a837d65bc8ff90681dce85d556dfb9fd48624a86536cb1b0d15a640000000000ffffffff010cc0eb0b00000000160014a2966546fd2a5aa926c7d910ca13f75974e103c702483045022100dbaf74cd714d0fc65415cfbd3261048db839cad5baaae9aae73b16e8c5dd06cb02203496cbc6faa8cb3785a7d50ec8ed76d7ce604df050b1708eba43920d0189c8e00121036309919df2569609d858f6ea944541ed325944005141933e1a01f810c07c94df00000000","020000000001013154531584818cbb37002e8c8be7f45250dd3a138df69e9ccc711ff40354c79b0000000000ffffffff021027000000000000225120be6162509c3f2ea87f728dcc48b6f5def8126379145ff77115bd8f7cf7bd556f0897eb0b00000000160014a2966546fd2a5aa926c7d910ca13f75974e103c702483045022100d0ed0504da5f30fb58c700bdf7bcaa6c7d4a037c575c178d599d153d56c50c28022024fcc9f9ab5da2ccf0da5e9c504812ed176f49897779bdeaf60345934d47fee5012103cf060f40607471f24a30343bf78f5a892bac2ef2294156fae4bb7ef13701539000000000","02000000000102cd8d7ba39ab8e27c3c2da97a373024982a1fcfc797981410d32787a8f803020e0000000000ffffffff0ec4b551340016d3fd5e2ad332c49dceeab2d78b51a3802425b9d8bf001b045f0000000000ffffffff02e803000000000000225120be6162509c3f2ea87f728dcc48b6f5def8126379145ff77115bd8f7cf7bd556f1c25000000000000225120be6162509c3f2ea87f728dcc48b6f5def8126379145ff77115bd8f7cf7bd556f0140391cf4773c49344d9086b3d6e374b6a6606256b6bb077b1664899c894fdbf2652319e8ba2b0381ccd64ba742ca74b57e1d0893c5596e470b6b17c353c2f59a670140e7d508c7199b3f3583cec6dc0be36e49b34799daf05c784ad3314d6b932fc90a9848fa1709ae16b56fe769d78cb9732e5e52b6b5753d26917a9d1d87e353947600000000","020000000001010b142b59fb2e54ec92522c74df6449413ccb09ca80ddfec26b9c3b3fed4540ff0100000000ffffffff02dc050000000000002251204fee1e5136f04b68289f95e4c3b5268564acc19c220788026c27f4f91761525c4c1d000000000000225120be6162509c3f2ea87f728dcc48b6f5def8126379145ff77115bd8f7cf7bd556f01401bd5b482e3faf1e15bdb6258156cba1d6091b7127f2510e8bad6c909d3c5b5083b249aaa914b7140a0a583783c3ffc1872a29cb26b146d189c3b42e6d153014b00000000","020000000001020b142b59fb2e54ec92522c74df6449413ccb09ca80ddfec26b9c3b3fed4540ff0000000000ffffffff609d519caf540c3f6ceb4bd703d5856402a6699ac6770218e65238c6e3ea409a0000000000ffffffff02e803000000000000225120be6162509c3f2ea87f728dcc48b6f5def8126379145ff77115bd8f7cf7bd556fe803000000000000225120be6162509c3f2ea87f728dcc48b6f5def8126379145ff77115bd8f7cf7bd556f0140cb8bbb02bc01c87a844e28a1e52ec71f1b4020c13df3910128e6367796bd0f48e1cb4c3155a6b3f5c897550701d92fbafcdb5f41e39fffb20968768edc2ea155034036d45920ee847fa755adab11c8773e6d2ae362addceabe20ce70cdac78498b3fa5e79278e01125ed7a9f77647ef9a4682a1e54f43a6206bbfff7de2f2a4e56e9fd130120be6162509c3f2ea87f728dcc48b6f5def8126379145ff77115bd8f7cf7bd556fac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e8030103208518a8e03fa517e339c2b84914e80c92c46772ecedf3fef4d55619dc4e519b3d01054c75a3626f7066646563696465657469746c6578184c696e65616765206765747320697473206f776e206f7267637768797844497473206f776e207265706f206f6e20546561687562206e6f772c206c696e656167652e6c6c63206c617465723a2061646f707465642c206e6f7420646572697665642e01070a6c696e656167652f7630001f646563696465204c696e65616765206765747320697473206f776e206f72676821c0be6162509c3f2ea87f728dcc48b6f5def8126379145ff77115bd8f7cf7bd556f00000000","020000000001020b9e954f16fa6056ec151afe5e9ffd756b4791660b4a4da137f4f2e6635d81530100000000ffffffff0ec4b551340016d3fd5e2ad332c49dceeab2d78b51a3802425b9d8bf001b045f0100000000ffffffff03e803000000000000225120be6162509c3f2ea87f728dcc48b6f5def8126379145ff77115bd8f7cf7bd556f8096980000000000160014b9af7e8f72352b0b03a1f5c37a677320c03fb53e94fe520b00000000160014a2966546fd2a5aa926c7d910ca13f75974e103c7014064bee65ae5ab9732e2642163ce7b9e09d4238505583ea19a7744713bb3269978fa17e8287c26c34f890887e44caa79df613006550f9d178ea7ff3fac55b14c6d024730440220231ccdb7ee04a22bb339a9181b2fc8fd0d93453817faaffad4408fc0d7bc0b2002201438bc9e89420b5ad8deec656ddec8775f3fd4e819c86ff5d957386ed3027ae4012103cf060f40607471f24a30343bf78f5a892bac2ef2294156fae4bb7ef13701539000000000"],"hash":"c0f3bddb7fcc3d984b99c132b735a196b0eca004325d1cdfc783400ac7042a56"},{"height":7,"previous":"c0f3bddb7fcc3d984b99c132b735a196b0eca004325d1cdfc783400ac7042a56","time":1792054035016,"raws":["02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff191873757065722d746573746e65742d6c6f63616c2d76303a37fdffffff0100f2052a0100000022512050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000","02000000000101d9ecf09e9da5ce30d23a10318b6fc8d7ab7be6db5521164382b8ea2c17ff29c70100000000ffffffff021a0900000000000022512011b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e129fcaf9a3b00000000225120870491e699efcba9a12b1a23e4b860b22cfddcd9798a9122068a9132f2979e2b0140250cf2623f05b5a5abc6bde27de743dd983c5fa9d13e4f0358f863ad18c49fd5d187c079397038f724aa83f98ed8ae8cc757c6c21d894b977c7568833b17887900000000","02000000000101f474cddd905c8e8ee934a03f0eecac8fe4ad477fbfd3c16e5d930651b477fff90000000000ffffffff02dc0500000000000022512049e206440fa9ab270cb3bd31e6a1ff2b99fa71a210c8db377ed4ecd9af75eb5c4a0100000000000022512011b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e129014021318e1ac2181b40de101feecd7e540f5398339e50f6422938c9692aa067bf15c26efa4fa0d374127c3b5962f6806659249dbd849e3a0d466ecfb7e446b8082300000000","0200000000010176fd590cd4a522b2ad58d15c58b68d1b8ab405e1a0876278e436fdcffa829ad50000000000ffffffff01e80300000000000022512011b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e1290340c3c6c14c1b8b86f0d05b97e4f55ad6ff7bae21721b0567ca4fdbaca0531d3c73b38c769d7fce279a1650f5ee6f70085a57eb8090d77126d82f7b9630b9f514ed742011b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e129ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010514a2626f7065666f756e64646e616d656461636d6501070a6c696e656167652f7630000a666f756e642061636d656821c111b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e12900000000","02000000000101f474cddd905c8e8ee934a03f0eecac8fe4ad477fbfd3c16e5d930651b477fff90100000000ffffffff020065cd1d00000000160014cfe1cf15b2932fe90f997fca93a8c86af834ba8d0849cd1d00000000225120870491e699efcba9a12b1a23e4b860b22cfddcd9798a9122068a9132f2979e2b0140b1c649575c41ba4009e83df739f934fad3c293e7eefe265a0038dd915d4ef5ec5f900f7c21cb842136a3d66504d974d1ea84cd62c9545e233231d240c583e6d200000000"],"hash":"5a3179da1bc71c2b0eb4b49a23b39a0f65e4de7b5962474e4d9c9cbfc0285aee"},{"height":8,"previous":"5a3179da1bc71c2b0eb4b49a23b39a0f65e4de7b5962474e4d9c9cbfc0285aee","time":1792054035379,"raws":["02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff191873757065722d746573746e65742d6c6f63616c2d76303a38fdffffff0100f2052a0100000022512050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000","02000000000101d56c3353cf7763a8efd2320459031ffced7b99b7d8532f2c2d42b892303e619e0000000000ffffffff02102700000000000022512011b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e129fc3bcd1d00000000160014cfe1cf15b2932fe90f997fca93a8c86af834ba8d02483045022100ab1928c56c11a41c4bec014a3ea4eaaa023b9fef59d0bbf36d60a4f1d517da220220556f63b80e4be7038c2de747cfd0fbd9eff3b5a39ad97840e37dec66359c0786012103bc57de44e90942234eef8b613d46853b8d2e5bb13d402364a00a8d1f5fc86eae00000000","02000000000101cbc72a6e54c12c0cfa0827d122dfb80910fc9e761132ba706e53b81f191ab3760000000000ffffffff02dc05000000000000225120ed7e4d734fec560c676d9fa5cc6e6daad80365b104699b95df4b9560e63d1727401f00000000000022512011b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e1290140ffd31421f3c97782fffa73c0e3bf934d57e4ea08da5f3d415adae569c26e171523820fe0ce733c217354014acbfaa64b31d2d1d2975bf65ef38c4bcd1d39c3a200000000","020000000001029cb6048a97835ff7d894fedaf6383440eb644859d9e3de657a7a76815b76e1990000000000ffffffff973f856458d1d7c8f46cd45e7ec7f017c926e0d340da4e1f7b89ba65e482bb060000000000ffffffff02e80300000000000022512011b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e129e80300000000000022512011b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e1290140c1eeb53d8e7d41de6b583964d4caf0b9e407eaf089d7420327aa98a8f11b37329f6b51fe9c332660a9bdfd4544bc61e098d5f749c0b2852e47cfb67e3d6e475a0340275f9a4d173d866354f84a993979fc8adb56f61c1c204999a0ddc9f2ed4e2c3d7cade7aed0fba57b3024096acd026b0bbb5431eb4d3062a609f6d2c6a14b7256a92011b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e129ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e8030103209cb6048a97835ff7d894fedaf6383440eb644859d9e3de657a7a76815b76e199010520a3626f70656269727468646e616d656573636f7574646b696e64656167656e7401070a6c696e656167652f7630000b62697274682073636f75746821c011b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e12900000000","0200000000010200e23594abef1046504d8cead07715fce44bf53c38ab1e09b80d5084717aa0f70100000000ffffffff973f856458d1d7c8f46cd45e7ec7f017c926e0d340da4e1f7b89ba65e482bb060100000000ffffffff02e80300000000000022512088cff875711f59e8ac1eeb6ba976e647385460e3edf43267055d72ff1760e84c4c1d00000000000022512011b7a193777c15d1573bd101ec29ec727f9658fde03a81f0b58c2b9eb233e12901403cf3973bf44620d5a9b59819feb22d9c5cfc3dcbd1f4a919d743c8ece8b2db0f7adc63a21790e9b2124f068254025d5cd34bcd0e635c3bcba464a56e1f5c3a7c0140627795fc719bc37b7629efe09f2dc7f6e736087c4a4555499e38c5b7f9b6b7980045bc7cac73e319eb0481ec51e7ebbb6446a345c23557c322416d21ac466ba900000000","02000000000101cbc72a6e54c12c0cfa0827d122dfb80910fc9e761132ba706e53b81f191ab3760100000000ffffffff0200e1f505000000001600142e77eb0fb084fd7988250ea371d5da34a6c1f6540859d71700000000160014cfe1cf15b2932fe90f997fca93a8c86af834ba8d024830450221008349ac46f60cb3b0e741ba27af3169faf85d1f1cabb5e98a95f2043f6729d8ea022055fb1e556bc3434a6ee0f75adbc4ea16dbbe252388738a6ca990b01014212a46012103bc57de44e90942234eef8b613d46853b8d2e5bb13d402364a00a8d1f5fc86eae00000000"],"hash":"e6615fe4a26cf5d0162dffc98988cd81a4ba17428ac53f7ace3403964d5cb699"},{"height":9,"previous":"e6615fe4a26cf5d0162dffc98988cd81a4ba17428ac53f7ace3403964d5cb699","time":1792054035735,"raws":["02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff191873757065722d746573746e65742d6c6f63616c2d76303a39fdffffff0100f2052a0100000022512050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac000000000","0200000000010137c92661e137457324e4f8d1de662aaa4ca2bf86dd587bb831e90e0f6a16a54b0000000000ffffffff028096980000000000160014980717ab75e3c13baa4705e3f57d190edadbd4cd8c485d05000000001600142e77eb0fb084fd7988250ea371d5da34a6c1f65402473044022001cc45bf024de0e47be8702a48f0bb6d31be430c574d7df084d2794e2535582e02200c5653d1b111a52ecbfd3e1ce5b5e2d933de3c9520a5e474c422202c47d73ad2012103d6486c9eb6ae20b3512d668c1051c0bba1858405dada93d40a551a5c44be988f00000000","02000000000101d4fbe90c215faa155a298f04ddbc91076b818ca102a437975e807b3f68d5de900100000000ffffffff02dc0500000000000022512057dd43ac7fc223eb95cc9aaff6cc4c09affdafdb44cdf885b05df0c1931a5eeae803000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc01403493a9ea5a6d8ca6017e6867f4b1a618ef96ae883a945b8484474c3079340b675d3b09e5f5be93cb9c8a5af448847d5daaced1b654e78bcaee7f53a1023ece4200000000","02000000000102451fc6049aa8dfe6216acf4286eca053927375208dcb68fdc6047d181f2b8f1c0000000000ffffffff6a7172d3426e7ef892af66526339212fa1dd1d0fa4841e1311687b3b7ba958d90000000000ffffffff02e803000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafce803000000000000225120ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc0140eb272e6a06766fc6367e3c0cbdf26332e884ba9b4d2c48b6b9e4c4271fb5c8686b49fc38d6f3cdd59b9fd9f7ee3841c770a0aec556c5665ba26efb6ef94dae4003407ac1881cdcff191fe299f5e5012b14eb2c9c10a8c48968093af17df3c23bc9c64bceee28915cc2f77c6708da428ec264b49fbe98cc4c76ea78820695421a7b52fa20ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafcac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38010202e80301032085e609fee30a238f3438a5cfa3f298b50041d629e8c6c49bd919ebfa0c4d1dac01054c69a4626f7065617761726462746f784266376130376137313834353030646238303931656162333833636635346265346663313537376430656138633464353034363130656661623934333565323030693065736b696c6c666c697374656e6462656c7465776869746501070a6c696e656167652f763000126177617264206c697374656e2077686974656821c1ac74efaa41a3f1901d54fedf9dab6b613a9c818979508874b6e68451f200eafc00000000"],"hash":"fc72f2039ee2677dda615e49ceef696480ad8990a04a82fdf2ef0a888b16f8fb"}],"mempool":[],"imports":[{"txid":"c822db91d422885d61ff591a74992a1bb3eafc2abf2a540f75ad427756fa8788","vout":0,"value":2000000000,"script":"5120870491e699efcba9a12b1a23e4b860b22cfddcd9798a9122068a9132f2979e2b"},{"txid":"35f6b02d7fac41f1275cb590c1100fff07ff66c7c8b82318c5c99f754e69465d","vout":1,"value":2000000000,"script":"5120870491e699efcba9a12b1a23e4b860b22cfddcd9798a9122068a9132f2979e2b"},{"txid":"c153b61eefe51ee3af36567a3bc5eb5bc22b3f9873f0d43608a4d45d07716b82","vout":1,"value":2000000000,"script":"5120870491e699efcba9a12b1a23e4b860b22cfddcd9798a9122068a9132f2979e2b"},{"txid":"fc2b8cb13a4720f8f8b14a13bac2ee92558a0e3af1c7846ef7970eb46a1be7a8","vout":1,"value":1000000000,"script":"5120870491e699efcba9a12b1a23e4b860b22cfddcd9798a9122068a9132f2979e2b"}]}} diff --git a/tests/indexer.test.js b/tests/indexer.test.js new file mode 100644 index 0000000..020e88f --- /dev/null +++ b/tests/indexer.test.js @@ -0,0 +1,78 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { Script, Tap, Tx } from '@cmdcode/tapscript'; +import { LocalChain, tapscriptValid } from '../src/adapters/chain.js'; +import { TestWallet } from '../src/adapters/wallet.js'; +import { InscriptionAdapter, POSTAGE, FEE, envelope } from '../src/adapters/inscription.js'; +import { MetadataIndex } from '../src/adapters/metadata-index.js'; +import { getVin, getVout } from '../src/upstream/primitives.js'; +import { census, rows } from '../src/orgchain/lineage.js'; +import { ordClient, ordEvents, ordState } from '../src/indexer/ord.js'; +import { compareWithLab } from '../src/indexer/compare.js'; + +// What ord answered after tools/regtest/rehearse.mjs, and the lab's public chain (tools/regtest/record.mjs). +const fixture = JSON.parse(readFileSync(new URL('./fixtures/regtest-rehearsal.json', import.meta.url), 'utf8')); +const replay = async url => { const hit = fixture.ord[new URL(url).pathname]; return new Response(hit?.body ?? '', { status: hit?.status ?? 404 }); }; +const draw = (s, root) => rows(s, root).map(r => (r.type === 'group' ? `${r.guide}${r.label}` : `${r.guide}${r.path} ${r.node.name}${r.away ? ' · graduated' : ''}`)); +const NUMS = ['OP_1', '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0']; +const hex = script => Script.encode(script, false).hex; + +test('real ord indexed the regtest rehearsal, and the same reducer folds it into the trees the lab drew', async () => { + const events = await ordEvents(ordClient('http://ord.test', replay), { from: fixture.from }); + assert.equal(events.length, 21); + assert.ok(events.every(e => e.protocol === 'lineage/v0' && e.parents <= 1 && /^[0-9a-f]{64}:\d+$/.test(e.location))); + const s = ordState(events), id = name => [...s.nodes.values()].find(n => n.name === name).id; + assert.ok(s.log.every(e => e.ok)); + assert.deepEqual(s.roots.map(r => r.name), ['yolopreneur', 'lineage', 'acme']); + assert.deepEqual(draw(s, id('yolopreneur')), ['● yolopreneur', '└─ orgs', " ├─ 0' well-known", ' │ └─ orgs', " │ ├─ 0' mailr", + " │ └─ 1' lineage · graduated", " └─ 1' time", ' ├─ agents', " │ ├─ 0' luna", " │ ├─ 1' sol", " │ └─ 2' astra", + ' └─ orgs', " ├─ 0' docscope", " ├─ 1' dollhouse", " ├─ 2' doodleloop", " └─ 3' hearthframe"]); + assert.deepEqual(census(s, id('lineage')), { orgs: 0, agents: 0, belts: 0, decisions: 1, closed: 0 }); + assert.deepEqual(s.nodes.get(id('scout')).badges.map(b => [b.skill, b.belt, s.nodes.get(b.by).name]), [['listen', 'white', 'well-known']]); + assert.deepEqual([...s.items.values()].map(i => [i.name, s.nodes.get(i.maker).name]), [['launch-kit', 'sol']]); + // Byte for byte: the same events in the same beats, the same metadata and parents, every inscription in the same + // output (the launch kit where lineage's deal put it). Only the miner's order within blocks differs. + const { problems, reordered } = compareWithLab({ chain: new LocalChain(fixture.chain) }, events); + assert.deepEqual(problems, []); + assert.equal(reordered, 9); +}); + +test('a reveal commits to its exact script (BIP-341), and the lab still loads reveals made the old way', () => { + const chain = new LocalChain(), w = TestWallet.create(); + chain.mine(w.script); + new InscriptionAdapter(chain, w).inscribe('found x', null, { op: 'found', name: 'x' }, { metaprotocol: 'lineage/v0' }); + const prevout = raw => { const out = Tx.decode(raw).vout[0]; return { value: Number(out.value), scriptPubKey: out.scriptPubKey }; }; + const reveal = Tx.decode(chain.mempool[1]); + reveal.vin[0].prevout = prevout(chain.mempool[0]); + assert.equal(tapscriptValid(reveal, 0), true); + // Before v1.9 the leaf came from tapscript's encodeScript, which re-encodes the hex script; real Bitcoin rejected that. + const old = new LocalChain(); + old.mine(w.script); + const [coin] = old.coins(w.script), script = envelope(w.pubkey, 'found y', null, { op: 'found', name: 'y' }, { metaprotocol: 'lineage/v0' }); + const leaf = Tap.encodeScript(script), [tapkey, control] = Tap.getPubKey(w.pubkey, { target: leaf }); + const commit = Tx.create({ vin: [getVin(coin.txid, coin.vout, coin.value, coin.scriptPubKey)], + vout: [getVout(POSTAGE + FEE, ['OP_1', tapkey]), getVout(coin.value - POSTAGE - 2 * FEE, w.script)] }); + commit.vin[0].witness = [w.sign(commit)]; + const legacy = Tx.create({ vin: [getVin(Tx.util.getTxid(commit), 0, POSTAGE + FEE, ['OP_1', tapkey])], vout: [getVout(POSTAGE, w.script)] }); + legacy.vin[0].witness = [w.sign(legacy, leaf, 0), Script.encode(script, false).hex, control]; + const raws = [Tx.encode(commit).hex, Tx.encode(legacy).hex], check = Tx.decode(raws[1]); + check.vin[0].prevout = prevout(raws[0]); + assert.equal(tapscriptValid(check, 0), false); + old.submitPackage(raws); // still accepted, so labs saved before v1.9 load + assert.equal(new MetadataIndex(old).events.length, 1); +}); + +test('a chain can start from coins imported from a real chain, and keeps them through a rebuild', () => { + const chain = new LocalChain(), w = TestWallet.create(), coin = { txid: 'ab'.repeat(32), vout: 1, value: 2e8, script: hex(w.script) }; + chain.importCoin(coin); + assert.throws(() => chain.importCoin(coin), /already in this chain/); + assert.throws(() => chain.importCoin({ ...coin, vout: 2, value: 51e8 }), /Invalid imported coin/); + const id = new InscriptionAdapter(chain, w).inscribe('found x', null, { op: 'found', name: 'x' }, { metaprotocol: 'lineage/v0' }); + chain.mine(NUMS); + const copy = new LocalChain(chain.snapshot()); + assert.deepEqual(copy.imports, [coin]); + assert.deepEqual(copy.imported(coin.txid, 1), { value: 2e8, scriptPubKey: coin.script }); + assert.deepEqual(new MetadataIndex(copy).events.map(e => [e.id, e.status]), [[id, 'confirmed']]); + assert.equal(copy.coins(w.script).length, 2); // the reveal's postage and the commit's change +}); diff --git a/tools/regtest/README.md b/tools/regtest/README.md new file mode 100644 index 0000000..73a09a8 --- /dev/null +++ b/tools/regtest/README.md @@ -0,0 +1,46 @@ +# Regtest rehearsal + +Lineage is an ordinals metaprotocol (`SPEC.md`). This folder checks that on real Bitcoin Core and real `ord`: the lab's +own action code makes the yolopreneur sample, Bitcoin Core (regtest, standardness on) accepts its transactions byte +for byte, `ord` indexes the inscriptions, and the same reducer folds ord's view back into the same trees. + +Regtest is local and private: nothing is downloaded, blocks are mined on demand, and the data folders stay a few MB. +Test coins only. + +## Start + +``` +brew install bitcoin ord +mkdir -p ~/lineage-regtest/bitcoin ~/lineage-regtest/ord +cp tools/regtest/bitcoin.conf ~/lineage-regtest/bitcoin/ +bitcoind -datadir=$HOME/lineage-regtest/bitcoin -daemon +bitcoin-cli -datadir=$HOME/lineage-regtest/bitcoin createwallet funder +ord --regtest --bitcoin-data-dir $HOME/lineage-regtest/bitcoin --data-dir $HOME/lineage-regtest/ord \ + server --address 127.0.0.1 --http-port 8088 --polling-interval 1s & +``` + +`bitcoin.conf` keeps mainnet's standardness rules (`acceptnonstdtxn=0`) and lowers the minimum relay fee to 0.1 sat/vB, +since the lab pays a fixed 500 sats per transaction. + +## Rehearse, index, compare + +``` +node tools/regtest/rehearse.mjs # nine beats; saves the lab to tools/regtest/out/lab.json +node bin/lineage-index.mjs --from 100 --compare tools/regtest/out/lab.json +node tools/regtest/record.mjs # refresh tests/fixtures/regtest-rehearsal.json (public data only) +``` + +The rehearsal funds the lab wallet with real regtest coins (`LocalChain.importCoin`) and pays every local beat to an +unspendable key, so nothing it signs spends a coin regtest doesn't have. After each beat it broadcasts that beat's +transactions and mines one regtest block. `--compare` checks that every event landed in the same beat with the same +metadata and parent, that every inscription sits in the same output, and that both fold into the same trees. Within a +block the miner may order independent events differently from the lab; that changes nothing (`SPEC.md`, section 2). + +`out/` holds the lab backup, test keys included, and is not committed. + +## Stop + +``` +bitcoin-cli -datadir=$HOME/lineage-regtest/bitcoin stop +pkill -f "ord --regtest" +``` diff --git a/tools/regtest/bitcoin.conf b/tools/regtest/bitcoin.conf new file mode 100644 index 0000000..d1501d8 --- /dev/null +++ b/tools/regtest/bitcoin.conf @@ -0,0 +1,13 @@ +regtest=1 +server=1 +txindex=1 +fallbackfee=0.0001 +[regtest] +rpcbind=127.0.0.1 +rpcallowip=127.0.0.1 +rpcport=18443 +port=18444 +listen=0 +acceptnonstdtxn=0 +minrelaytxfee=0.000001 +blockmintxfee=0.000001 diff --git a/tools/regtest/record.mjs b/tools/regtest/record.mjs new file mode 100644 index 0000000..58bda2b --- /dev/null +++ b/tools/regtest/record.mjs @@ -0,0 +1,16 @@ +// Record what ord answered about a rehearsal, with the lab's public chain (no keys), as a fixture the unit tests replay +// without bitcoind or ord: tests/fixtures/regtest-rehearsal.json. Usage: node tools/regtest/record.mjs [lab.json] [from] +import { readFileSync, writeFileSync } from 'node:fs'; +import { decodeLab } from '../../src/orgchain/store.js'; +import { ordClient, ordEvents } from '../../src/indexer/ord.js'; + +const base = 'http://127.0.0.1:8088', from = Number(process.argv[3] ?? 120), ord = {}; +const lab = decodeLab(readFileSync(process.argv[2] ?? 'tools/regtest/out/lab.json', 'utf8')); +const record = async (url, options) => { + const res = await fetch(url, options), body = await res.text(); + ord[new URL(url).pathname] = { status: res.status, body }; + return new Response(body, { status: res.status }); +}; +const events = await ordEvents(ordClient(base, record), { from }); +writeFileSync('tests/fixtures/regtest-rehearsal.json', `${JSON.stringify({ from, ord, chain: lab.chain.snapshot() })}\n`); +console.log(`recorded ${Object.keys(ord).length} ord answers for ${events.length} events, and ${lab.chain.blocks.length} local blocks`); diff --git a/tools/regtest/rehearse.mjs b/tools/regtest/rehearse.mjs new file mode 100644 index 0000000..c53f4fa --- /dev/null +++ b/tools/regtest/rehearse.mjs @@ -0,0 +1,107 @@ +// Rehearse the yolopreneur sample on real Bitcoin Core (regtest). The lab's own action code signs every transaction on +// a LocalChain whose only money is coins imported from regtest, so nothing it makes spends a local coinbase. After each +// beat, that beat's transactions go to bitcoind byte for byte and are mined into a block; ord then indexes them like +// any other inscriptions, and bin/lineage-index.mjs folds them back into trees. Test coins on a private chain only. +// Usage: node tools/regtest/rehearse.mjs [out.json] (tools/regtest/README.md starts bitcoind and ord) +import { mkdirSync, writeFileSync } from 'node:fs'; +import { dirname } from 'node:path'; +import { Script, Tx } from '@cmdcode/tapscript'; +import { TestWallet } from '../../src/adapters/wallet.js'; +import { MetadataIndex } from '../../src/adapters/metadata-index.js'; +import { emptyLab, encodeLab, setRule, setTempo } from '../../src/orgchain/store.js'; +import { accept, act, deposit, offer, send } from '../../src/orgchain/actions.js'; +import { SAT } from '../../src/orgchain/money.js'; +import { lineageEvents, reduce, rows } from '../../src/orgchain/lineage.js'; +import { bitcoind } from './rpc.mjs'; + +const out = process.argv[2] ?? 'tools/regtest/out/lab.json'; +const NUMS = ['OP_1', '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0']; // BIP-341's key that no one holds +const hex = script => Script.encode(script, false).hex; +const node = bitcoind(), funder = bitcoind({ wallet: 'funder' }); + +const lab = emptyLab(); +lab.wallet = TestWallet.create(); +setRule(lab, 'none'); // no holding mines: the only money is what regtest sends in +setTempo(lab, 'manual'); // the rehearsal decides when a beat lands +// Local beats pay the unspendable key, so no local coinbase is ever spent; anything else beating is a bug here. +const mineTo = lab.chain.mineTo.bind(lab.chain); +lab.chain.mineTo = vout => { + if (vout.some(o => hex(o.scriptPubKey) !== hex(NUMS))) throw new Error('Out of lab money: only the rehearsal beats, and only to the unspendable key.'); + return mineTo(vout); +}; + +const miner = await funder('getnewaddress', 'miner', 'bech32m'); +const mine = n => node('generatetoaddress', n, miner); +const height = await node('getblockcount'); +if (height < 120) await mine(120 - height); // mature coins for the funder, and past ord's regtest jubilee +const funding = []; +for (const btc of [20, 20, 20, 10]) funding.push(await funder('sendtoaddress', lab.wallet.address, btc)); +await mine(1); +for (const txid of funding) { + const o = (await node('getrawtransaction', txid, true)).vout.find(v => v.scriptPubKey.hex === hex(lab.wallet.script)); + lab.chain.importCoin({ txid, vout: o.n, value: Math.round(o.value * SAT), script: o.scriptPubKey.hex }); +} +console.log(`lab wallet ${lab.wallet.address} holds ${funding.length} coins from regtest`); + +let beats = 0, sent = 0; +async function beat(label) { + lab.chain.mine(NUMS); + const raws = lab.chain.blocks.at(-1).raws.slice(1); + for (const raw of raws) { + try { await node('sendrawtransaction', raw, 0); } + catch (error) { + if (!/cluster|too-long-mempool-chain/i.test(error.message)) throw new Error(`beat ${beats + 1} (${label}), ${Tx.util.getTxid(raw)}: ${error.message}`); + await mine(1); // a long chain of cued spends: let regtest confirm what it has, then carry on + await node('sendrawtransaction', raw, 0); + } + } + const [hash] = await mine(1), block = await node('getblock', hash); + for (const raw of raws) { + if (!(await node('getrawtransaction', Tx.util.getTxid(raw), true)).confirmations) throw new Error(`${Tx.util.getTxid(raw)} did not confirm.`); + } + beats++; sent += raws.length; + console.log(`beat ${beats}: ${label} · ${raws.length} transactions → regtest block ${block.height}`); +} + +const yolo = act(lab, 'found', null, { name: 'yolopreneur' }); +deposit(lab, yolo, 30 * SAT); +deposit(lab, yolo, 30 * SAT); +await beat('found yolopreneur and fund it'); +const wk = act(lab, 'birth', yolo, { name: 'well-known', kind: 'org' }), time = act(lab, 'birth', yolo, { name: 'time', kind: 'org' }); +await beat('well-known and time'); +send(lab, yolo, time, 40 * SAT); +send(lab, yolo, wk, 10 * SAT); +act(lab, 'birth', wk, { name: 'mailr', kind: 'org' }); +const lineage = act(lab, 'birth', wk, { name: 'lineage', kind: 'org' }); +const luna = act(lab, 'birth', time, { name: 'luna', kind: 'agent' }), sol = act(lab, 'birth', time, { name: 'sol', kind: 'agent' }); +act(lab, 'birth', time, { name: 'astra', kind: 'agent' }); +await beat('two sends and five births'); +send(lab, wk, lineage, 2 * SAT); +for (const name of ['docscope', 'dollhouse', 'doodleloop', 'hearthframe']) act(lab, 'birth', time, { name, kind: 'org' }); +await beat("time's ventures"); +send(lab, time, sol, SAT); +send(lab, time, luna, 0.5 * SAT); +act(lab, 'award', time, { to: sol, skill: 'launch', belt: 'green' }); +act(lab, 'award', time, { to: luna, skill: 'post', belt: 'white' }); +act(lab, 'decide', yolo, { title: 'Every venture gets a page', why: '.yolopreneur.lol, served from one pages branch.' }); +const kit = act(lab, 'make', sol, { name: 'launch-kit', note: 'The page, the post and the checklist every venture launches with.' }); +await beat('belts, a decision and an item'); +act(lab, 'graduate', wk, { of: lineage }); +act(lab, 'decide', lineage, { title: 'Lineage gets its own org', why: 'Its own repo on Teahub now, lineage.llc later: adopted, not derived.' }); +accept(lab, offer(lab, kit, lineage, 0.1 * SAT).txid); +await beat('lineage graduates and buys the launch kit'); +const acme = act(lab, 'found', null, { name: 'acme' }); +deposit(lab, acme, 5 * SAT); +await beat('another holding'); +const scout = act(lab, 'birth', acme, { name: 'scout', kind: 'agent' }); +send(lab, acme, scout, SAT); +await beat('an agent of acme'); +send(lab, scout, wk, 0.1 * SAT); +act(lab, 'award', wk, { to: scout, skill: 'listen', belt: 'white' }); +await beat('scout pays well-known, which awards it a belt'); + +const live = reduce(lineageEvents(new MetadataIndex(lab.chain)), { pending: true }); +for (const root of live.roots) console.log(rows(live, root.id).map(r => (r.type === 'group' ? `${r.guide}${r.label}` : `${r.guide}${r.path} ${r.node.name}`)).join('\n')); +mkdirSync(dirname(out), { recursive: true }); +writeFileSync(out, encodeLab(lab)); +console.log(`${beats} beats, ${sent} transactions confirmed on regtest (height ${await node('getblockcount')}); the lab is saved to ${out}`); diff --git a/tools/regtest/rpc.mjs b/tools/regtest/rpc.mjs new file mode 100644 index 0000000..6f3e247 --- /dev/null +++ b/tools/regtest/rpc.mjs @@ -0,0 +1,20 @@ +// A small JSON-RPC client for the local regtest bitcoind (tools/regtest/README.md), authenticated with its cookie. +import { readFileSync } from 'node:fs'; +import { homedir } from 'node:os'; +import { join } from 'node:path'; + +export const DATADIR = process.env.LINEAGE_REGTEST ? join(process.env.LINEAGE_REGTEST, 'bitcoin') : join(homedir(), 'lineage-regtest', 'bitcoin'); + +export function bitcoind({ url = 'http://127.0.0.1:18443', datadir = DATADIR, wallet = null } = {}) { + let id = 0; + return async (method, ...params) => { + const cookie = readFileSync(join(datadir, 'regtest', '.cookie'), 'utf8').trim(); + const res = await fetch(wallet ? `${url}/wallet/${wallet}` : url, { + method: 'POST', headers: { Authorization: `Basic ${Buffer.from(cookie).toString('base64')}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ jsonrpc: '1.0', id: ++id, method, params }), + }); + const body = await res.json().catch(() => null); + if (!body || body.error) throw new Error(`${method}: ${body?.error?.message ?? `HTTP ${res.status}`}`); + return body.result; + }; +}