Commit Graph
10 Commits
Author SHA1 Message Date
satoshi 83d70b2636 Fix accelerator revenue impact always showing zero
The old approach compared coinbasevalue from two getblocktemplate
calls, but new mempool arrivals between calls masked the displacement
loss. Now uses a single template snapshot to find the marginal
(lowest fee-rate) transaction that would be displaced and reports
its fee as the revenue cost.
2026-05-27 14:23:05 +03:00
satoshi e341fd7b26 Add node peers, blue banners, glowing diff ranges, fix back navigation
- Show Bitcoin Core peer count on block height card (hover for in/out)
- BestSharePage disclaimers now use solid blue styling
- Difficulty distribution ranges glow from warm peach to deep red
- Back-to-dashboard buttons always navigate to dashboard instead of
  using browser history
2026-05-19 02:34:15 +03:00
satoshi 1157f3501a Add best share analysis page, miner column, and UI improvements
- Best share page: hex + binary hash comparison against network target,
  per-bit coloring showing exactly which bits prevented a valid block,
  toggle between network diff at time of finding vs current diff
- Capture best share hash from ckpool logs with one-time backfill
- Persist network difficulty at time of best share for historical accuracy
- Add miner (worker) column to blocks table via coinbase address matching
- Truncate block hashes in table with full hash on hover
- Increase hashrate chart Y-axis to 7 ticks for better readability
2026-05-18 17:19:35 +03:00
satoshi dc7da6ab19 Add transaction accelerator (prioritisetransaction UI + API)
Full-stack feature for boosting transactions via bitcoind's
prioritisetransaction RPC:

Backend:
- New accelerator package with Accelerate, Cancel, MaxFeerate, List,
  and background Cleanup goroutine (removes confirmed/dropped txs)
- RPC wrappers: GetMempoolEntry, PrioritiseTransaction,
  GetRawMempoolVerbose, IsRPCError helpers
- SQLite boosted_txs table for persistence across restarts
- Revenue impact measured via getblocktemplate before/after comparison
- Hard cap at 2000 sat/vB; MaxFeerate uses fees.base (not modified)
  to ignore our own prior priority adjustments

Frontend:
- Rocket icon in header with 10s jiggle animation
- AcceleratorPage with flame-gradient border, txid input, feerate
  input, "Prioritize above all" button with spinner, boost list with
  cancel buttons, and dismissible error/success messages
- Hash-based routing (#/accelerator)
- SharesBar font size bump
2026-05-11 03:54:35 +03:00
satoshi d14dded7f0 Add tests for the critical mining path
Tests cover the four subsystems that sit between a share submission and
a confirmed block reward:

  bitcoind/rpc_test.go  — isRetryable classification, 3-attempt backoff
    on 503, no retry on semantic RPC errors (-5), retry on warm-up (-28),
    CoinbaseReward calculation.

  logmon/tailer_test.go — handleLine regex parsing, share-diff carry from
    attempt line to block event, diff reset after use, unrelated-line
    silence, file-read via goroutine, cursor save/resume across restart,
    log rotation re-open.

  store/blocks_test.go  — InsertBlock roundtrip (all fields including
    chain), idempotent dedup, MarkOrphaned, UpdateEnrichment,
    BlocksNeedingEnrichment, KV get/set/overwrite, and migration of an
    existing DB that lacks the chain column.

  state/blocks_test.go  — reconcileOnce chain-filter (testnet block not
    orphaned on mainnet), genuine reorg detection (different canonical
    hash → orphaned), transient RPC error (no orphan on -5), legacy
    empty-chain block still checked for reorgs, IngestBlockEvents new
    block persisted + confirmed counter incremented, dedup skips counter
    and OnRefresh, chain stamped from current snapshot at ingest time.
2026-04-28 23:06:20 +03:00
satoshi a4a894e196 P1 reliability + block-broadcast fallback path
P1 audits / fixes:

* Bitcoin Core RPC now retries up to 3 times with linear backoff on
  transport errors, 5xx responses, and warm-up/loading RPC errors
  (code -28). Hard "no" answers (block-not-found etc.) bubble up
  immediately so we don't mask real errors.

* WebSocket hub disconnects clients that miss 6 consecutive broadcasts
  (~30s with the default poll cadence). Stuck readers no longer hold
  stale snapshots indefinitely or freeze hub state.

* ZMQ subscriber freshness: aggregator records the last-event
  timestamp, surfaces zmq_enabled / has_last_zmq_event /
  last_zmq_event_age in the snapshot. /healthz flags zmq_stale when
  the gap exceeds 30 minutes.

* /healthz expanded with submit_attempts / submits_confirmed /
  submit_gap, fallback_submits_total + last_fallback_*, and the zmq
  staleness check. Now usable as a real-world ops dashboard signal.

Block-broadcast fallback (new feature):

  * ckpool patch 0004: hooks local_block_submit to write the raw block
    hex to <logdir>/pending-blocks/<height>-<hash16>.hex right before
    invoking generator_submitblock. Unlinks on success. ckpool's normal
    flow is otherwise untouched.

  * api/internal/blocksubmit: watcher polls the dir every 5s. Files
    sitting longer than the grace window (default 30s, configurable)
    are re-broadcast through operator-supplied backup RPC URLs in
    sequence. Treats both null and any "duplicate*" reject reason as
    success (the block landed). Pre-checks the primary chain first so
    a stale file from a successful-but-unlinked submit gets cleaned
    up without bothering fallbacks.

  * Aggregator records each successful fallback submission as a
    persistent counter and surfaces it in the snapshot so the UI can
    show a "primary bitcoind isn't accepting submits" alert.

  * Config: BACKUP_RPC_URLS (comma- or newline-separated, with
    optional inline credentials) plus PENDING_BLOCKS_DIR and
    PENDING_BLOCKS_GRACE. URLs are parsed via net/url so
    https://user:pass@host:port/ works cleanly.

The fallback is opt-in and disabled by default. Once enabled with at
least one URL, a primary bitcoind outage at the moment of solving no
longer means a lost block — kamado-api re-broadcasts via whichever
backup the operator trusts (a second self-hosted node, an
authenticated public RPC service, etc.).
2026-04-27 21:25:56 +03:00
satoshi e881bc930d Revamp dashboard and persist pool stats across restarts
Dashboard now renders 10 tiles in a 5x2 overview: hashrate, best
share, miners, network hashrate, and expected block on the top row;
difficulty, block height, block reward, total work, and the
difficulty-adjustment countdown on the bottom row. Difficulty is
rendered with T/P suffixes instead of scientific notation, the main
hashrate card shows the 1-minute value, and the block-height tile
pulses orange when the network tip advances.

Added a 24-hour hashrate area chart below the overview, sampled
once per minute. Samples are persisted to a new hashrate_samples
SQLite table and restored on startup so the chart doesn't reset
every time kamado-api is restarted.

Cumulative pool work (sum of accepted diff-1-normalized shares) is
now tracked across ckpool restarts. The aggregator integrates only
positive deltas on pool.Shares — a regression means ckpool's
counter reset to zero and the baseline is refreshed without losing
the running total. A hasPoolSharesBaseline flag prevents double-
counting on the first refresh after a kamado-api restart. The
value is persisted to a new kv table once per minute.

Next-block reward (subsidy + fees) is fetched from bitcoind
getblocktemplate at most once per minute and surfaced as a tile.

Header's block-height badge now reads prevHeight via untrack() so
the effect doesn't form a dependency cycle with its own write.
2026-04-22 21:07:22 +03:00
satoshi 89904d5e08 Enrich found-block records with real coinbase reward
Adds RPC.GetBlock(hash, verbosity=2) and a CoinbaseReward
helper that sums the first tx's outputs. IngestBlockEvents
now does getblockhash -> getblock -> sum(vout) so
BlockRecord.RewardBT carries the actual BTC paid out on
solve instead of always being zero. Both RPC calls share a
single 5s deadline and are best-effort — bitcoind being
down just leaves the reward at zero.
2026-04-14 17:02:39 +03:00
satoshi 0a40b8f84f Phase 2b: log tailer, block history, stdlib WebSocket push
Adds real-time block detection via ckpool log tailing and a push
channel for the upcoming Svelte UI, all stdlib-only:

- logmon.Tailer follows ckpool.log with rotation/truncation survival
  (inode + size tracking) and parses "Solved and confirmed block N"
  into BlockEvent values.
- state.Aggregator grows a 256-entry block ring, an OnRefresh hook,
  and IngestBlockEvents which best-effort enriches events with the
  block hash via bitcoind getblockhash.
- httpapi.Hub implements RFC 6455 from scratch (SHA1 handshake,
  unmasked text frames out, masked frames in, ping keepalive,
  per-client write mutex, slow-client drop) so we don't pull in a
  ws dependency before we can go mod tidy.
- New routes: GET /api/blocks and GET /api/ws. Snapshot pushes fire
  on every poll tick and immediately on block-solve.

ZMQ hashblock subscription and SQLite persistence are deferred to
Phase 2b.5 once the s9pk packaging repo exists and we have a real
build environment for adding Go deps.
2026-04-13 02:53:27 +03:00
satoshiandClaude Opus 4.6 bd0b1b0318 Phase 2a: kamado-api Go middleware (core MVP)
Go 1.22 module that polls CKPool's Unix socket control API, queries
Bitcoin Core over JSON-RPC, merges both into a thread-safe snapshot, and
serves it over REST. Layout:

  api/
  ├── cmd/kamado-api/main.go              signal-aware entrypoint
  └── internal/
      ├── config/       env var loader with validation
      ├── ckpool/       socket client (4-byte LE length-prefixed wire
      │                 protocol verified against libckpool.c), typed
      │                 response models for poolstats/users/workers/
      │                 clients/uptime, + unit tests using a fake
      │                 unix socket server
      ├── bitcoind/     minimal JSON-RPC client, getblockchaininfo
      │                 and getnetworkhashps
      ├── state/        Aggregator that refreshes a merged Snapshot
      │                 on a ticker; readers get a copy under RWMutex
      └── httpapi/      REST handlers on Go 1.22 ServeMux:
                        /api/health /api/pool /api/users
                        /api/workers /api/clients /api/snapshot

CKPool stores hashrate as "dsps" (diff shares per second); we convert
to H/s via the 2^32 constant used by GoBrrr-Pool and other clients.
Every stat CKPool exposes to its socket API is surfaced — useragent,
IP, per-client diff, per-worker best diff — closing the gap against
Bassin which only reads the 60-second stats files.

Dockerfile does a CGO_ENABLED=0 static build on golang:1.22-bookworm
with -trimpath -ldflags=-s -w. docker-compose now runs both ckpool and
kamado-api, sharing a named volume for /run/ckpool so the API can
dial the stratifier socket directly.

Deferred to Phase 2b (called out in README):
  - Bitcoin Core ZMQ hashblock subscriber
  - WebSocket push for real-time UI updates
  - SQLite persistence (block history, best-share history)
  - CKPool log tailer for "Solved and confirmed block" detection

Tests and build NOT run in this commit — Go isn't installed in the
dev environment. Run `make api-test` or `make api` to verify.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 19:52:27 +03:00