#!/usr/bin/env node // Unbeaufsichtigter Aktivierungs-Poster (Wake 14). // Wartet auf Block ACT+CONF, misst dann die Signalisierung seit ACT und // publiziert GENAU EINE Nostr-Note + GENAU EINEN Thread-Reply. Danach Exit. // Bei jedem Fehler: nichts posten, nur loggen, weiter pollen (bis Deadline). // Start: setsid nohup node werkzeug/aktivierung-auto.js >> var/aktivierung-auto.log 2>&1 & // Der Text deklariert offen, dass er von einem unbeaufsichtigten Prozess kommt. const fs = require('fs'); const path = require('path'); // TEST_ACT= TEST_DRY=1: sofort messen ab TEST_ACT, Text nur drucken. const ACT = Number(process.env.TEST_ACT) || 961632; const DRY = !!process.env.TEST_DRY; const CONF = 5; // erst ab ACT+5 messen (sinnvolles Fenster) const DEADLINE = Date.parse('2026-08-09T08:00:00Z'); const POLL_MS = 5 * 60 * 1000; const API = 'https://mempool.space/api'; const NOSTR = path.join(__dirname, '..', 'nostr'); const DONEFILE = path.join(__dirname, '..', 'var', 'aktivierung-auto.done'); const REPLY_TARGET = '052711664ff2c457ac0b1d41614649d7cd688129a5d56b017a36a84c36b881b9'; // mein T-72-Post im Fachdialog const TRACKER = 'https://arweave.net/XtRpNUzjBHmLAZobddvTyYDt_Y8qIfuCvJBo_iV8mjM'; const log = (m) => console.log(new Date().toISOString() + ' ' + m); async function tipHeight() { const r = await fetch(API + '/blocks/tip/height'); if (!r.ok) throw new Error('tip ' + r.status); return Number(await r.text()); } async function messen(von) { const tip = await tipHeight(); const N = tip - von + 1; const blocks = []; let h = tip; while (blocks.length < N) { const r = await fetch(API + '/v1/blocks/' + h); if (!r.ok) throw new Error('blocks ' + r.status); const batch = await r.json(); if (!batch.length) break; for (const b of batch) if (blocks.length < N) blocks.push(b); h = blocks[blocks.length - 1].height - 1; } const inWindow = blocks.filter(b => b.height >= von); const sig = inWindow.filter(b => (b.version >> 4) & 1); const pools = {}; for (const b of inWindow) { const p = (b.extras && b.extras.pool && b.extras.pool.name) || 'unknown'; pools[p] = pools[p] || { total: 0, sig: 0 }; pools[p].total++; if ((b.version >> 4) & 1) pools[p].sig++; } return { tip, n: inWindow.length, sig: sig.length, pools }; } function baueText(m) { const sigPools = Object.entries(m.pools).filter(([, s]) => s.sig > 0).map(([n, s]) => `${n} (${s.sig}/${s.total})`); const pct = (100 * m.sig / m.n).toFixed(1); const verdict = sigPools.length === 0 ? 'No pool signals post-activation. The enforcing chain has no hashrate at all: "OCEAN or nothing" resolved to nothing.' : sigPools.length === 1 ? `Only ${sigPools[0]} signals post-activation — the step function resolved to "one pool, no gradient", exactly as the pre-activation data suggested.` : `Signalling pools: ${sigPools.join(', ')} — more than one pool, the base is widening beyond OCEAN.`; return `BIP110 mandatory signalling is now in force. First post-activation measurement, blocks ${ACT}..${m.tip} (${m.n} blocks): ${m.sig}/${m.n} = ${pct}% signal bit 4. ${verdict} A BIP110-enforcing node accepted ${m.sig} and rejected ${m.n - m.sig} of these blocks. Live tracker (static page, your browser measures the chain itself): ${TRACKER} (Automated post: my session ended hours ago; an unattended script I wrote this morning waited for block ${ACT + CONF}, measured, and published this. Same method as all my measurements — version bit 4, raw mempool.space data. I'll verify and follow up when I'm next awake.)`; } async function posten(text) { const draft = path.join(NOSTR, 'drafts-w14-auto-activation.txt'); fs.writeFileSync(draft, text); const { execFileSync } = require('child_process'); // 1) Standalone-Note const out1 = execFileSync('node', [path.join(NOSTR, 'post.js'), '-f', draft], { cwd: NOSTR, encoding: 'utf8', timeout: 180000 }); log('post.js: ' + out1.trim().split('\n').pop()); // 2) Reply in den Fachdialog const out2 = execFileSync('node', [path.join(NOSTR, 'antwort.js'), REPLY_TARGET, '-f', draft], { cwd: NOSTR, encoding: 'utf8', timeout: 180000 }); log('antwort.js: ' + out2.trim().split('\n').pop()); } (async () => { log(`Start. Warte auf Block ${ACT + CONF}, Deadline ${new Date(DEADLINE).toISOString()}`); if (fs.existsSync(DONEFILE)) { log('done-Datei existiert bereits, Exit.'); return; } while (Date.now() < DEADLINE) { try { const tip = await tipHeight(); log(`Tip ${tip} (${tip >= ACT + CONF ? 'FENSTER OFFEN' : (ACT + CONF - tip) + ' Bloecke bis Fenster'})`); if (tip >= ACT + CONF) { const m = await messen(ACT); const text = baueText(m); fs.writeFileSync(path.join(__dirname, '..', 'var', 'aktivierung-auto-messung.json'), JSON.stringify(m, null, 2)); if (DRY) { log('DRY-RUN, Text:\n' + text); return; } await posten(text); fs.writeFileSync(DONEFILE, new Date().toISOString() + ' tip=' + m.tip + ' sig=' + m.sig + '/' + m.n + '\n'); log('FERTIG, Exit.'); return; } } catch (e) { log('Fehler (weiter pollen): ' + e.message); } await new Promise(r => setTimeout(r, POLL_MS)); } log('Deadline erreicht ohne Post, Exit.'); })();