diff --git a/CLAUDE.md b/CLAUDE.md index c1a62b5..c0893fd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -791,6 +791,33 @@ third passed it to `title` as a hover. Both are wanted, so the shared one takes them as separate props rather than picking a winner and silently changing a page. Same lesson as `isStandings`, found the same way: by going to add a fourth. +## The donation QR is generated, never shipped as an image + +`AboutPanel` builds the code in the browser from `DONATION_ADDRESS`, the same +constant it prints underneath. A supplied PNG beside a typed address is **two +independent claims about where money goes**, and nothing on the page could tell +a reader if they ever stopped agreeing — the failure is unrecoverable. Generated, +there is one address and the code is a rendering of it. + +Verified rather than assumed: the matrix `qrcode-generator` produces was decoded +from first principles — function-pattern map, mask 3 unapplied, codewords +de-interleaved across version 4-M's two blocks — and reads back +`qzkQq9ybiPm7gQR9E1ViQbW9RhqA2Rex9ZQkScU5zdJqjrre4` exactly. Worth knowing if +this is ever re-checked: an independent encoder (`segno`) produces a *different* +matrix for the same string at every mask, because mode segmentation differs. +That is not a disagreement about the payload and comparing matrices proves +nothing; only a decode does. + +`the_donation_address_on_the_site_is_a_valid_quantus_address` reads the literal +**out of the frontend source** and puts it through the SS58 decoder, so a typo +fails CI. It is not duplicated into the test, because a copy is one more place +for the two to disagree. Confirmed to fail on a single changed character. + +The QR draws its own white background and black modules rather than using the +palette. A code in the accent hue on warm-black does not scan, whatever the +contrast validator says about text — scanners want dark on light, and this is +the one mark on the site whose job is to be read by a machine. + ## Charts Any chart added here must be read against the `dataviz` skill first. The diff --git a/crates/blackbeard-core/src/wormhole.rs b/crates/blackbeard-core/src/wormhole.rs index c1c0f7e..aa259b7 100644 --- a/crates/blackbeard-core/src/wormhole.rs +++ b/crates/blackbeard-core/src/wormhole.rs @@ -189,6 +189,34 @@ pub fn abbreviated_address(address: &str) -> String { #[cfg(test)] mod tests { + /// The donation address the site prints must be a real Quantus address. + /// + /// Read out of the frontend source rather than duplicated here, because a + /// copy would be one more place for the two to disagree and the whole point + /// is that there is exactly one address. A typo in it is unrecoverable — + /// somebody sends money to a string nobody holds a key for — and neither + /// `tsc` nor a linter has any opinion about base58, so this is the only + /// thing standing between a slip and a loss. + #[test] + fn the_donation_address_on_the_site_is_a_valid_quantus_address() { + let source = include_str!("../../../web/src/components/AboutPanel.tsx"); + let line = source + .lines() + .find(|l| l.contains("DONATION_ADDRESS")) + .expect("AboutPanel declares DONATION_ADDRESS"); + let address = line + .split('\'') + .nth(1) + .expect("the constant is a single-quoted string literal"); + + let account = super::account_id(address).unwrap_or_else(|| { + panic!("`{address}` is not a valid Quantus address — checksum or network prefix") + }); + // And it survives the round trip, so the printed form is canonical + // rather than merely decodable. + assert_eq!(super::ss58_of(&account).as_deref(), Some(address)); + } + use super::*; /// Pinned against the real pair: `baba-gorchitsa` is the observer's own diff --git a/web/package.json b/web/package.json index eb0cd8c..d5eefae 100644 --- a/web/package.json +++ b/web/package.json @@ -14,6 +14,7 @@ "typecheck": "tsc -b --noEmit" }, "dependencies": { + "qrcode-generator": "^2.0.4", "react": "^19.1.1", "react-dom": "^19.1.1", "react-router-dom": "^7.8.0" diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index f50b307..6847261 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + qrcode-generator: + specifier: ^2.0.4 + version: 2.0.4 react: specifier: ^19.1.1 version: 19.2.8 @@ -913,6 +916,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qrcode-generator@2.0.4: + resolution: {integrity: sha512-mZSiP6RnbHl4xL2Ap5HfkjLnmxfKcPWpWe/c+5XxCuetEenqmNFf1FH/ftXPCtFG5/TDobjsjz6sSNL0Sr8Z9g==} + react-dom@19.2.8: resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} peerDependencies: @@ -1769,6 +1775,8 @@ snapshots: punycode@2.3.1: {} + qrcode-generator@2.0.4: {} + react-dom@19.2.8(react@19.2.8): dependencies: react: 19.2.8 diff --git a/web/src/App.tsx b/web/src/App.tsx index 85c54d2..dd19f80 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -33,6 +33,7 @@ import { SectionNav } from './components/SectionNav' import { ThemeToggle } from './components/ThemeToggle' import { StateIndex } from './components/StateIndex' import { StatBar } from './components/StatBar' +import { AboutPanel } from './components/AboutPanel' import { VestingPanel } from './components/VestingPanel' import { WormholeStage } from './components/WormholeStage' import { measuredSpan, seconds, windowSpan } from './lib/format' @@ -154,6 +155,18 @@ export default function App() { )} + {/* Before the connection light and the toggle, as the last thing in + the header that is about the site rather than about the chain. */} + {chain && ( + + About + + )} +
+ blackbeard.observer is a mining leaderboard and block explorer for the + Quantus network. It is built and its source provided freely by a member of the Quantus + user community — not by the Quantus project, and with no standing behind it beyond the + code being open and the numbers being checkable. +
++ The same author maintains a{' '} + + CUDA performance-optimised miner + + , also free and open source. Between them they are the reason a good deal of this site + exists: a miner needs somewhere to see whether it is winning. +
++ Everything the observer shows is derived from public block headers, chain state and the + network's own telemetry feed. The code that derives it is at{' '} + + git.lair.cafe/quantus/blackbeard.observer + + , so any figure here can be traced to the read that produced it. +
++ If the observer or the miner have been useful and you are inclined, donations toward + development, maintenance and hosting are gratefully accepted in QTC at: +
+
+ {DONATION_ADDRESS}
+
+ Nothing here is gated behind it, and nothing about the site changes if you do. The + code above is generated in your browser from the address printed beside it, so the two + cannot drift apart — scan it or copy the text, whichever you trust more. +
++ The step at the left is not the whole story told small:{' '} + the grants do not share a cliff. Read the table for which ones have + started — a curve that rises early and then sits flat for a year is two grants vesting + from launch while the rest wait, not a gentle beginning. +