pages: the live supertestnet.lol build (main 00e93f5) with its security headers
This commit is contained in:
commit
b7cee409ad
18 files changed
+391
No files matched your search
@@ -0,0 +1,4 @@
|
||||
# supertestnet/app · pages
|
||||
|
||||
The built site (`npm run build` → `dist/`) that Teahub pages serves at https://supertestnet.lol, plus `_headers`.
|
||||
Source is `main`. To publish: build on `main`, copy `dist/` here, commit, push.
|
||||
@@ -0,0 +1,12 @@
|
||||
# Same headers the old Node server (server.js on main) sent; read by Teahub pages.
|
||||
/*
|
||||
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: blob:; frame-src 'self' blob:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'
|
||||
X-Frame-Options: DENY
|
||||
Referrer-Policy: no-referrer
|
||||
Permissions-Policy: camera=(), microphone=(), geolocation=()
|
||||
Cache-Control: no-cache
|
||||
/assets/*
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
/recursive-preview.html
|
||||
Content-Security-Policy: default-src 'none'; script-src 'nonce-local-ord-preview'; worker-src blob:; style-src 'unsafe-inline'; connect-src 'none'; img-src data:; form-action 'none'; base-uri 'none'; frame-ancestors 'self'
|
||||
X-Frame-Options: SAMEORIGIN
|
||||
@@ -0,0 +1 @@
|
||||
:root{font-family:Arial,Helvetica,sans-serif;font-synthesis:none;--orange:#f7931a;--orange-2:#ffad42;--bg:#0f1113;--panel:#171a1e;--panel-2:#20242a;--text:#f3f5f7;--muted:#9ca4ad;--line:#30363d;--soft:#23272e;--danger:#ff9b8d;color:var(--text);background:var(--bg)}:root[data-theme=light]{--bg:#f5f3ef;--panel:#fffdf9;--panel-2:#f0ece5;--text:#1a1a1a;--muted:#6f6a63;--line:#d8d1c8;--soft:#ece7df;--danger:#a54536}*{box-sizing:border-box}html{background:var(--bg);color-scheme:dark}html[data-theme=light]{color-scheme:light}body{margin:0;background:var(--bg);color:var(--text);transition:background .18s,color .18s}a{color:var(--orange);text-underline-offset:4px}button,input,textarea{font:inherit}.file-button,button{cursor:pointer}button{border:1px solid var(--line);border-radius:8px;background:var(--panel-2);color:var(--text);padding:12px 17px;font-weight:600;transition:background .15s,border-color .15s,transform .15s}button:hover:not(:disabled){border-color:var(--orange);transform:translateY(-1px)}button:disabled{opacity:.4;cursor:not-allowed;transform:none}button.primary{background:var(--orange);color:#1c1207;border-color:var(--orange)}button.primary:hover:not(:disabled){background:var(--orange-2)}.primary span{float:right;margin-left:20px}.quiet{background:transparent;border-color:var(--line);font-size:13px}button:focus-visible,a:focus-visible,summary:focus-visible,.file-button:focus-visible{outline:3px solid var(--orange-2);outline-offset:4px}header{max-width:1240px;margin:auto;padding:24px 32px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line)}.brand{text-decoration:none;color:var(--text);display:flex;align-items:center;gap:10px;font-size:15px;font-weight:800;letter-spacing:1px}.mark{display:inline-block;flex-shrink:0;background:var(--orange);width:34px;height:34px;border-radius:8px}.version{font:11px monospace;background:var(--soft);color:var(--orange);border-radius:4px;padding:3px 5px}.header-actions{display:flex;align-items:center;gap:14px}.network{font:11px monospace;color:var(--muted);letter-spacing:1px}.network i{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--orange);margin-right:6px}.theme-toggle{padding:8px 10px;font:11px monospace}main{max-width:1176px;padding:0 0 10px;margin:auto}.hero{display:flex;justify-content:space-between;align-items:center;padding:53px 0 36px}.eyebrow{font:11px monospace;letter-spacing:1.8px;color:var(--orange);margin:0 0 19px}h1{font-size:clamp(42px,5.5vw,70px);font-weight:500;line-height:1.02;letter-spacing:-3px;margin:0 0 24px}.intro{color:var(--muted);font-size:16px;line-height:1.65}.orbit{width:255px;height:220px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:36px;background:radial-gradient(ellipse,color-mix(in srgb,var(--orange) 16%,transparent),transparent 70%);margin-right:65px}.cube{display:grid;place-items:center;width:110px;height:110px;border:1px solid var(--orange);box-shadow:13px 13px color-mix(in srgb,var(--orange) 18%,var(--panel)),14px 14px color-mix(in srgb,var(--orange) 48%,var(--panel));transform:rotate(-12deg);background:var(--panel-2);color:var(--orange)}.orbit span{font:10px monospace;letter-spacing:2px;color:var(--muted)}.notice{background:color-mix(in srgb,var(--orange) 7%,var(--panel));border:1px solid color-mix(in srgb,var(--orange) 24%,var(--line));border-radius:8px;padding:14px 18px;font-size:12px;color:var(--muted);line-height:1.6}.notice strong{color:var(--text)}#status{min-height:52px;padding:16px 2px;font-size:13px;color:var(--orange)}#status.error{color:var(--danger)}.workspace{display:grid;grid-template-columns:1fr 1fr;gap:18px}.panel{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:24px;min-width:0}.panel-title{display:flex;align-items:center;gap:10px;margin-bottom:22px}.panel-title h2{font-size:17px;margin:0;font-weight:500;letter-spacing:-.4px}.step{font:11px monospace;color:var(--orange);background:color-mix(in srgb,var(--orange) 10%,var(--panel));border:1px solid color-mix(in srgb,var(--orange) 28%,var(--line));border-radius:6px;padding:7px}.tag{margin-left:auto;font:9px monospace;letter-spacing:1px;color:var(--muted);border:1px solid var(--line);padding:5px;border-radius:4px}.panel p{line-height:1.6;font-size:14px;color:var(--muted)}.label{display:block;font:10px monospace!important;letter-spacing:1.5px;color:var(--muted);margin:0 0 10px}.balance{display:flex;align-items:baseline;gap:12px;font-size:37px;letter-spacing:-1px;font-variant-numeric:tabular-nums}.balance small{color:var(--orange);font-size:13px;letter-spacing:1px}.muted{color:var(--muted)!important;font-size:12px!important;line-height:1.65}label{display:block;font-size:12px;color:var(--muted);margin:14px 0 9px}input,textarea{width:100%;color:var(--text);background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:12px;outline:none}input{font:11px monospace}textarea{resize:vertical;min-height:100px;line-height:1.5}inputLine truncated
|
||||
@@ -0,0 +1,10 @@
|
||||
(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))r(s);new MutationObserver(s=>{for(const i of s)if(i.type==="childList")for(const o of i.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(s){const i={};return s.integrity&&(i.integrity=s.integrity),s.referrerPolicy&&(i.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?i.credentials="include":s.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(s){if(s.ep)return;s.ep=!0;const i=n(s);fetch(s.href,i)}})();function lo(t){return typeof t=="string"&&/^(?:\/content\/[0-9a-f]{64}i(?:0|[1-9][0-9]*)|\/r\/(?:inscription|metadata|parents|children)\/[0-9a-f]{64}i(?:0|[1-9][0-9]*)|\/r\/tx\/[0-9a-f]{64}|\/r\/blockheight|\/r\/blockhash\/(?:0|[1-9][0-9]*)|\/r\/blockinfo\/(?:0|[1-9][0-9]*|[0-9a-f]{64}))$/.test(t)}const nf=`<h2>Recursive local inscription</h2><p>Live data from this browser's chain:</p>
|
||||
<script>
|
||||
const height = await (await fetch('/r/blockheight')).json();
|
||||
const me = await (await fetch('/r/inscription/' + ord.id)).json();
|
||||
const metadata = await (await fetch('/r/metadata/' + ord.id)).json();
|
||||
ord.render(JSON.stringify({ id: ord.id, height, sat: me.sat, metadata }, null, 2));
|
||||
<\/script>`;function rf(t,e){let n=0;const r=new Map;self.onmessage=({data:s})=>{const i=r.get(s.token);i&&(r.delete(s.token),i(s))},self.fetch=async(s,i={})=>{if(!e(s)||i.method&&i.method!=="GET")throw new TypeError("Only whitelisted local GET paths are allowed.");const o=++n,a=await new Promise(f=>{r.set(o,f),postMessage({type:"fetch",token:o,path:s})});if(a.error)throw new TypeError(a.error);const c=a.body instanceof Uint8Array?a.body:JSON.stringify(a.body);return new Response(c,{status:a.status,headers:{"Content-Type":a.contentType}})},self.ord=Object.freeze({id:t,contentPath:"/content/"+t,render:s=>postMessage({type:"render",text:String(s).slice(0,65536)})})}function sf(t,e,n){const r=`const recursivePath = ${lo.toString()}; (${rf.toString()})(${JSON.stringify(n)}, recursivePath); (async () => {${e.join(`
|
||||
;
|
||||
`)}
|
||||
})().catch(e => ord.render(e.message));`;return{markup:t,worker:r}}function of(t,e,n){const r=new MessageChannel;return r.port1.onmessage=({data:s})=>{if(!s||s.type!=="fetch")return;const i=lo(s.path)?e.request(s.path):{error:"Blocked local path."};r.port1.postMessage({...i,token:s.token})},t.contentWindow.postMessage({type:"local-ord",...n},"*",[r.port2]),()=>r.port1.close()}const ms=2048,af=new TextEncoder;function Dr(t){const e=[],n=(s,i)=>{if(i<24)e.push(s*32+i);else if(i<=255)e.push(s*32+24,i);else if(i<=65535)e.push(s*32+25,i>>>8,i&255);else if(i<=4294967295){const o=new Uint8Array(4);new DataView(o.buffer).setUint32(0,i),e.push(s*32+26,...o)}else{e.push(s*32+27);const o=new Uint8Array(8);new DataView(o.buffer).setBigUint64(0,BigInt(i)),e.push(...o)}},r=(s,i=0)=>{if(i>64)throw new Error("Metadata nesting exceeds 64 levels.");if(s===null)e.push(246);else if(typeof s=="boolean")e.push(s?245:244);else if(typeof s=="number"&&Number.isFinite(s))if(Number.isSafeInteger(s)&&!Object.is(s,-0))n(s>=0?0:1,s>=0?s:-1-s);else{const o=new Uint8Array(8);new DataView(o.buffer).setFloat64(0,s),e.push(251,...o)}else if(typeof s=="string"){const o=af.encode(s);n(3,o.length);for(const a of o)e.push(a)}else if(Array.isArray(s))n(4,s.length),s.forEach(o=>r(o,i+1));else if(typeof s=="object"&&Object.getPrototypeOf(s)===Object.prototype){const o=Object.entries(s);n(5,o.length);for(const[a,c]of o)r(a,i+1),r(c,i+1)}else throw new Error("Metadata must contain only JSON values.");if(e.length>ms)throw new Error(`Metadata must be at most ${ms} CBOR bytes.`)};return r(t),Uint8Array.from(e)}function ho(t){if(!t.trim())return;let e;try{e=JSON.parse(t)}catch{throw new Error("Invalid metadata JSON.")}return Dr(e),e}function cf(t){if(t=new Uint8Array(t),!t.length||t.length>ms)throw new Error("Invalid CBOR metadata size.");let e=0;const n=i=>{if(e+i>t.length)throw new Error("Truncated CBOR metadata.");const o=t.slice(e,e+i);return e+=i,o},r=(i=0)=>{if(i>64)throw new Error("Metadata nesting exceeds 64 levels.");const o=n(1)[0],a=o>>5,c=o&31;if(a===7){if(c===20)return!1;if(c===21)return!0;if(c===22)return null;if(c===27){const l=new DataView(n(8).buffer).getFloat64(0);if(Number.isFinite(l))return l}throw new Error("Unsupported CBOR value.")}let f=c;if(c>=24&&c<=27){f=0;for(const l of n(2**(c-24)))f=f*256+l}else if(c>=28)throw new Error("Unsupported CBOR length.");if(!Number.isSafeInteger(f))throw new Error("CBOR integer exceeds JSON precision.");if(a===0)return f;if(a===1)return-1-f;if(a===3)return new TextDecoder("utf-8",{fatal:!0}).decode(n(f));if((a===4||a===5)&&f>t.length)throw new Error("Invalid CBOR collection length.");if(a===4)return Array.from({length:f},()=>r(i+1));if(a===5){const l={};for(let u=0;u<f;u++){const h=r(i+1);if(typeof h!="string"||Object.hasOwn(l,h))throw new Error("Invalid CBOR JSON map key.");Object.defineProperty(l,h,{value:r(i+1),enumerable:!0,writable:!0,configurable:!0})}return l}throw new Error("Unsupported CBOR JSON type.")},s=r();if(e!==t.length)throw new Error("Trailing CBOR metadata.");return s}const Ui=5e9;function po(t,e,n){const r=[];for(const[s,i]of t){const o=i-s;if(e>=o){e-=o;continue}const a=Math.min(n,o-e);if(a&&r.push([s+e,s+e+a]),n-=a,e=0,!n)break}return r}function go(t,e){var n;return(n=po(t,e,1)[0])==null?void 0:n[0]}function Bn(t,e){let n=0;for(const[r,s]of t){if(e>=r&&e<s)return n+e-r;n+=s-r}return null}function yo(t,e,n,r){const s=r?[[(n-1)*Ui,n*Ui]]:e.flatMap(o=>o.ranges);let i=0;return t.vout.map(o=>{const a=po(s,i,Number(o.value));return i+=Number(o.value),a})}function bs(t){if(!Number.isSafeInteger(t)||t<0)throw new Error(`Wrong positive integer: ${t}`)}function ff(t){if(typeof t!="boolean")throw new Error(`Expected boolean, not ${t}`)}function mo(t,...e){if(!(t instanceof Uint8Array))throw new TypeError("Expected Uint8Array");if(e.length>0&&!e.includes(t.length))throw new TypeError(`Expected Uint8Array of length ${e}, not of length=${t.length}`)}function uf(t){if(typeof t!="function"||typeof t.create!="function")throw new Error("Hash should be wrapped by utils.wrapConstructor");bs(t.outputLen),bs(t.blockLen)}function lf(t,e=!0){if(t.destroyed)throw new Error("Hash instance has been destroyed");if(e&&t.finished)throw new Error("Hash#digest() has already been called")}function df(t,e){mo(t);const n=e.outputLen;if(t.length<n)throw new Error(`digestInto() expects output buffer of length at least ${n}`)}const hf={number:bs,bool:ff,bytes:mo,hash:uf,exists:lf,output:df};var Xr=hf;/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */const Qr=t=>new DataView(t.buffer,t.byteOffset,t.byteLength),Ge=(t,e)=>t<<32-e|t>>>e,pf=new Uint8Array(new Uint32Array([287454020]).buffer)[0]===68;if(!pf)throw new Error("Non little-endian hardware is not supported");Array.from({length:256},(t,e)=>e.toString(16).padStart(2,"0"));function gf(t){if(typeof t!="string")throw new TypeError(`utf8ToBytes expected string, got ${typeof t}`);return new TextEncoder().encode(t)}function bo(t){if(typeof t=="str
Line truncated
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"version":"0.2.4","url":"https://supertestnet.lol/downloads/inscriboor-extension-v0.2.4.zip"}n
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34 34"><rect width="34" height="34" rx="8" fill="#f7931a"/></svg>
|
||||
|
After Width: | Height: | Size: 119 B |
+61
@@ -0,0 +1,61 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<meta name="theme-color" content="#f7931a">
|
||||
<meta name="description" content="A private Bitcoin playground. Mine fake coins, inscribe a thought, and follow it into a block.">
|
||||
<title>Super Testnet — your own little Bitcoin universe</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<script type="module" crossorigin src="/assets/index-DclT5lE9.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-1LhHTydZ.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a class="brand" href="#"><span class="mark" aria-hidden="true"></span> SUPER TESTNET <span class="version">v0</span></a>
|
||||
<div class="header-actions"><span class="network"><i></i> PRIVATE LOCAL CHAIN</span><button id="theme-toggle" class="theme-toggle quiet" type="button" aria-label="Toggle color theme">☀ Light</button></div>
|
||||
</header>
|
||||
<main>
|
||||
<section class="hero"><div><p class="eyebrow">BITCOIN, WITH ROOM TO EXPERIMENT</p><h1>Your own little<br>Bitcoin universe.</h1><p class="intro">Mine a block. Inscribe a thought. See it land in your wallet.<br>All in your browser, all with pretend money.</p></div><div class="orbit" aria-hidden="true"><div class="cube"><svg viewBox="0 0 100 100" width="82" height="82" fill="none" stroke="currentColor" stroke-width="5" aria-hidden="true"><path d="M35 17v12m12-12v12M35 76v12m12-12v12M24 29h25c22 0 22 23 0 23H24m0 0h28c22 0 22 24 0 24H24m7-47v47"/></svg></div><span>YOUR CHAIN. YOUR PACE.</span></div></section>
|
||||
<aside class="notice"><strong>Test coins only.</strong> This is a private simulation, not Bitcoin testnet or mainnet. Local-wallet keys stay in this browser, unencrypted. Inscriboor keys stay in the extension. Never load real keys or send real funds here.</aside>
|
||||
<div id="status" role="status" aria-live="polite">Choose Inscriboor or a local test wallet to start your first loop.</div>
|
||||
<section class="workspace">
|
||||
<article class="panel wallet"><div class="panel-title"><span class="step">01</span><h2>Your test wallet</h2><span id="wallet-state" class="tag">NOT CREATED</span></div>
|
||||
<div id="empty-wallet"><p>One wallet. A whole chain to play with.</p><button id="connect-inscriboor" class="primary">Get Inscriboor ↓</button> <button id="create-wallet" class="quiet">Use local test wallet</button><p id="inscriboor-status" class="muted">Looking for the extension…</p><details class="install-help"><summary>Install Inscriboor</summary><ol><li>Download and unzip Inscriboor.</li><li>Open <code>chrome://extensions</code> (or Brave extensions).</li><li>Turn on Developer mode.</li><li>Choose <strong>Load unpacked</strong> and select the unzipped <code>extension</code> folder.</li><li>Refresh this page. The button above will change from <strong>Get Inscriboor</strong> to <strong>Connect Inscriboor</strong>.</li></ol></details></div>
|
||||
<div id="active-wallet" hidden><p class="label">FAKE BTC BALANCE</p><div class="balance"><span id="balance">0</span><small>BTC</small></div><p id="reserved" class="muted"></p><label for="address">Local regtest-format address</label><input id="address" readonly><button id="export-wallet" class="quiet">↓ Save test wallet</button></div>
|
||||
<div class="wallet-footer"><label class="file-button" for="load-wallet">↑ Load test wallet or lab backup</label><input id="load-wallet" type="file" accept=".json,application/json"><p class="muted">Saved on this device. Local-wallet backups contain their test key; Inscriboor backups contain only a public descriptor. A lab backup also preserves the chain.</p></div>
|
||||
</article>
|
||||
<article class="panel"><div class="panel-title"><span class="step">02</span><h2>Make some blocks</h2></div><p>Skip the faucet queue. Every block gives you <strong>50 fake BTC</strong>, ready to use immediately.</p><div class="chain-stats"><div><span class="label">BLOCK HEIGHT</span><strong id="height">0</strong></div><div><span class="label">PENDING TXS</span><strong id="pending">0</strong></div></div><button id="mine" class="primary" disabled>Mine now <span>+</span></button><label for="miner-interval">Auto miner</label><select id="miner-interval"><option value="0">OFF</option><option value="10">10s</option><option value="30">30s</option><option value="60">60s</option></select><p id="miner-status" aria-live="polite">Auto mining OFF</p><p class="muted">Keep a lab tab open; browser throttling may delay blocks. Mining also confirms your pending inscriptions.</p></article>
|
||||
<article class="panel inscribe"><div class="panel-title"><span class="step">03</span><h2>Leave a little something</h2><span class="tag">INSCRIPTION LAB</span></div><label for="mime-type">Content type</label><select id="mime-type"></select><label for="body-file">Or upload a local file (16 KiB maximum)</label><input id="body-file" type="file"><label for="parent">Parent inscription (optional, immutable)</label><select id="parent"><option value="">No parent</option></select><label for="delegate">Delegate inscription ID (optional, future IDs allowed)</label><input id="delegate" placeholder="64 hex characters + i0"><label for="reinscribe">Reinscribe an owned sat (optional)</label><select id="reinscribe"><option value="">New sat</option></select><label for="pointer">Advanced pointer (zero-based output sat position)</label><input id="pointer" inputmode="numeric" placeholder="Default first sat"><button id="recursive-demo" class="quiet">Use recursive demo</button><p class="muted">Recursive HTML/SVG scripts use local fetch and ord.render(text) in an isolated worker; ord.id is the requested inscription. No DOM or network access.</p><label for="payload">What will you put on your chain?</label><textarea id="payload" placeholder="Hello, little Bitcoin universe…" rows="3" disabled></textarea><label for="metadata">JSON metadata (optional, independent of body)</label><textarea id="metadata" rows="3" placeholder='{"protocol":"my-experiment","note":"hello"}'></textarea><p id="metadata-count" class="muted">0 / 2048 CBOR bytes · ord tag 5 · JSON is authoring format only</p><label for="metaprotocol">Metaprotocol (optional, independent field)</label><input id="metaprotocol" placeholder="e.g. tea-v1 · ord tag 7"><p class="muted">Native ord metaprotocol identifier. Metadata and inscription data remain independent.</p><div class="compose-footer"><span id="byte-count" class="muted">0 / 16384 bytes</span><button id="review" disabled>Review inscription ↗</button></div>
|
||||
<div id="review-panel" hidden><h3>Review before signing</h3><div id="review-preview"></div><pre id="review-text"></pre><p id="review-parent"></p><pre id="review-metadata"></pre><p>Two transactions: commit + reveal. <strong>1,000 fake sats</strong> in fees; <strong>1,000 fake sats</strong> in a new wallet output. Any selected parent or reinscription output is also returned in full; reinscription appends to its existing sat stack.</p><p id="signing-note" class="muted">The next button asks your active signer to approve commit + reveal, then submits them to your private chain.</p><button id="sign" class="primary">Sign & inscribe</button> <button id="cancel-review" class="quiet">Cancel</button></div>
|
||||
</article>
|
||||
</section>
|
||||
<section class="collection-section"><div class="section-title"><div><p class="eyebrow">04 / THE THINGS YOU MADE</p><h2>Your inscriptions <span id="collection-count">0</span></h2></div><span class="muted">Owned by this wallet</span></div><div id="collectibles" class="collectibles"><p class="empty">Your first inscription belongs here.<br><span>Fund your wallet, write some text, and make it yours.</span></p></div></section>
|
||||
<section class="explorer-section">
|
||||
<div class="section-title"><div><p class="eyebrow">05 / FOLLOW THE TRAIL</p><h2>Chain explorer</h2></div><button id="export-lab" class="quiet">↓ Save complete lab</button></div>
|
||||
<div class="chain-board">
|
||||
<div class="mempool-head"><div><span class="chain-kicker">MEMPOOL</span><strong>Waiting for the next block</strong></div><span class="muted">commit + reveal transactions live here before mining</span></div>
|
||||
<div id="mempool-list" class="mempool-list"></div>
|
||||
<div class="chain-divider"><span>↓ mine</span></div>
|
||||
<div class="blocks-head"><span class="chain-kicker">BLOCKS</span><span class="muted">newest first · click any block to inspect it</span></div>
|
||||
<div id="blocks" class="blocks"></div>
|
||||
</div>
|
||||
<article id="inspector" class="panel inspector" hidden tabindex="-1"></article>
|
||||
</section>
|
||||
<section class="panel" id="indexes"><h2>Indexes</h2>
|
||||
<p>Metadata is data; indexers are interpretation. Native metaprotocol tag 7 is authoritative; metadata.protocol remains a compatibility fallback. Raw events remain uninterpreted.</p>
|
||||
<p id="index-stats"></p><label for="protocol">Discovered protocol names</label><select id="protocol"></select>
|
||||
<p id="protocol-stats"></p><h3>Ordered raw events</h3><p class="muted">Confirmed blocks in chain order, then mempool order.</p><div id="protocol-events"></div>
|
||||
<label for="reducer">Optional trusted local indexer</label><select id="reducer"><option value="">None</option><option value="event-counter">Example: event-counter</option></select>
|
||||
<h3>Derived state (separate from raw events)</h3><pre id="derived-state">No reducer selected.</pre>
|
||||
</section>
|
||||
<section class="panel" id="api-explorer"><h2>API explorer <span class="tag">LOCAL / IN-BROWSER</span></h2>
|
||||
<p>LocalOrdAPI reads this browser’s chain and indexes. These paths are not server URLs; the static host cannot access your lab.</p>
|
||||
<label for="api-preset">Preset path</label><select id="api-preset"></select>
|
||||
<label for="api-path">Local API path</label><input id="api-path" value="/r/blockheight">
|
||||
<button id="api-try">Try</button> <button id="api-copy" class="quiet">Copy response</button>
|
||||
<pre id="api-response" aria-live="polite">Choose a path and press Try.</pre>
|
||||
</section>
|
||||
<details class="about"><summary>Under the hood & attribution</summary><p>Real serialized Bitcoin transactions, Schnorr signatures and Taproot commit/reveal scripts, on a deliberately simplified local UTXO chain. No proof of work, Bitcoin consensus, peer network, public broadcast, or production custody. Synthetic local sats follow FIFO input-to-output ranges. Child creation and reinscription preserve the owned sat. Fees retire the input tail; this is not Bitcoin consensus.</p><p>Built from and inspired by <a href="https://github.com/supertestnet/testnet_generator">Supertestnet’s testnet_generator</a>, <a href="https://github.com/supertestnet/inscriptions-online">inscriptions-online</a>, and <a href="https://github.com/supertestnet/vanilla-js-browser-wallet">vanilla-js-browser-wallet</a>. <a href="https://github.com/supertestnet/node_faker">node_faker</a> was evaluated for future RPC compatibility.</p><p>Transaction library: <a href="https://github.com/cmdruid/tapscript">tapscript by Christopher Scott</a> (<a href="/licenses/tapscript.txt">CC-BY-1.0</a>). <a href="/licenses/supertestnet-CC0.txt">Supertestnet CC0 license</a> · <a href="/licenses/noble-curves.txt">noble-curves license</a> · <a href="/licenses/noble-hashes.txt">noble-hashes license</a>.</p></details>
|
||||
<footer><span>SMALL CHAIN. BIG POSSIBILITIES.</span><button id="reset" class="quiet danger">Reset local lab</button></footer>
|
||||
</main>
|
||||
</body></html>
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the “Software”), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the “Software”), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
@@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
@@ -0,0 +1 @@
|
||||
<!doctype html><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-local-ord-preview'; worker-src blob:; style-src 'unsafe-inline'; connect-src 'none'; img-src data:; form-action 'none'; base-uri 'none'"><div id="markup"></div><pre id="recursive-output"></pre><script nonce="local-ord-preview" src="/recursive-preview.js"></script>
|
||||
@@ -0,0 +1,16 @@
|
||||
// Trusted renderer only. Inscription JavaScript runs exclusively in the worker.
|
||||
addEventListener('message', function start(event) {
|
||||
if (event.source !== parent || event.data?.type !== 'local-ord' || !event.ports[0]) return;
|
||||
removeEventListener('message', start);
|
||||
const port = event.ports[0];
|
||||
document.getElementById('markup').innerHTML = event.data.markup;
|
||||
const url = URL.createObjectURL(new Blob([event.data.worker], { type: 'text/javascript' }));
|
||||
const worker = new Worker(url);
|
||||
worker.onmessage = ({ data }) => {
|
||||
if (data.type === 'render') document.getElementById('recursive-output').textContent = String(data.text).slice(0, 65536);
|
||||
else if (data.type === 'fetch') port.postMessage(data);
|
||||
};
|
||||
worker.onerror = () => { document.getElementById('recursive-output').textContent = 'Recursive script failed.'; };
|
||||
port.onmessage = ({ data }) => worker.postMessage(data);
|
||||
addEventListener('pagehide', () => { worker.terminate(); port.close(); URL.revokeObjectURL(url); }, { once: true });
|
||||
});
|
||||
Reference in new issue
Block a user