Repository navigation
Not phase F. The oracle waits while what exists is made to earn its place: a
chain with traffic on it, the pool it waits in, two execution clients as well
as three consensus clients, a 3D scene that keeps time and names who is who,
pictures on the front page, and walkthroughs that CI now runs. Bringing Reth up
turned up the release's most consequential fix — the devnet's execution clients
had never peered.
Added
-
Reth on the devnet. Node 3 runs Reth under Teku instead of a third geth,
so client diversity now holds on both layers: a block Reth builds for Teku's
proposer has to be executed and accepted by the two geth nodes before
Lighthouse and Prysm attest to it, and the other way round. Verified on a
fresh devnet — blocks built by each accepted by the other, one head hash on
all three. A swap rather than a fourth node, because the 22/21/21 validator
split and the finality arithmetic the walkthroughs teach depend on there
being three.The flags follow geth's for the same reasons; the differences are written
beside them innetwork.yml(no init step, no tip floor to disable, archive
unless told otherwise, metrics at the root path, a health check over bash's
/dev/tcpbecause the image has no curl). The banner,network status,
walkthrough 4, the agreement table and the 3D scene's labels all name both
clients on each node, and tests check that the compose file,NODESand the
control room's copy agree. -
cupel traffic: a chain with something happening on it. Every picture of
an idle chain is the same flat line, so this sends what a used one carries —
ether payments, token mints and transfers, vault deposits and redemptions,
wrapping and unwrapping ether — from development accounts 0, 1 and 3 at once,
in bursts of none to five every second or two. Blocks stand tall or stay
empty, and gas varies because the work does. Account 2 never sends: it is the
signer's key, and its audit log stays about the signer.Now and then it does, on purpose, the two things people are most surprised
by: a transaction that reverts, which is still mined and still pays for its
gas, and one sent with a nonce too far ahead, which is accepted, parks in
queuedand runs only once the gap in front of it is filled. Each is
explained the first time it happens.--cleanturns both off, and a clean run
exits non-zero if anything reverted or was accepted and never included — the
second being how geth reports a tip below its floor, which is not at all. At
the end it asks for every receipt and says what became of each transaction.Everything is signed locally and sent raw, so it needs no Foundry, and it
works against either chain:--rpc http://127.0.0.1:8555sends to a devnet
node directly. It replaces the throwaway script that fed the 3D page. -
The 3D scene names its clients, and keeps time. In network mode each
marker carries its client's name —node1 Lighthouse— on a row of its own
with a line down to the marker, so the scene reads without the legend under
it. The labels are HTML moved to each marker's place on screen every frame,
so they stay crisp and follow the theme; rows are handed out from the right
so no line ever crosses a label.Blocks now sit at their place in time rather than one after another: one
box-width is the slot time on the devnet and the block time in the lab, a
slot nobody proposed in leaves a gap the width of a block, and a line across
the floor marks the moment each epoch began. The legend says how much time
the window covers — fifty seconds in the lab, ten minutes on the devnet —
which is the difference the two lanes used to hide. Absolute seconds were
tried on paper and rejected: at a fixed scale the devnet lane is twelve times
longer and every box a pixel wide. -
The pool, as a page. Every other page shows transactions that ran; this
one shows where they wait first.txpool_contentthrough the gateway, read
four times a second in lab mode: pending (ready for the next block),
queued (accepted and parked — with the nonce each one is waiting for,
worked out from the sender's pending run or its account nonce), and just
left, with how long each stayed. That last is shown as the range the
readings allow rather than a single number: two readings a few milliseconds
apart, one either side of a block, once timed everything at 0.0s. In network
mode it also shows each node's own counts, because there is no network-wide
pool — only each node's view of one. Calls are named (Token.transfer,
Vault.deposit) on this page and the transaction page.The selectors behind those names, and the ones the page already used to read
the contracts, are now checked against the bytecode in the committed genesis
file. A comment had claimed that check existed; nothing did it. -
The README shows the thing. A moving picture of the 3D page under
cupel trafficat the top, and the overview, a block and the walkthrough in
the control-room section, each in the reader's own theme. An audit flagged a
project about making machinery visible with not one picture of it on the
front page. CI now fails if the README names an image that is not in the
repository — a broken screenshot fails nothing on its own.docs/shoot.mjs
is how they were taken: a headless Chrome or Edge over the DevTools protocol,
nothing to install, with the commands for each image written at the top. -
The chain as a solid object. A page that draws the block window in three
dimensions: one box per block, running away from the camera in the order they
were made, as tall as the gas each used, orange where a block carried
transactions and grey where it was empty. Point at a block for its number,
transactions and gas; click it to open the block page. In network mode a
marker floats above the block each client calls the head, and a client whose
head is outside the window is drawn at the end of the lane and named as ahead
or behind rather than placed on a block the page never fetched.Nothing in the scene is ornament — the length is the window, the heights are
gas, the colour is whether anything was in the block. The height scale climbs
in round steps and the legend prints the number it has reached: scaled
straight to the busiest block in view, every box grows the moment that block
falls out of a window that moves every second, and a block that has not
changed appears to have.three.js is larger than the rest of the interface put together, so the page is
loaded on its own and only a reader who opens it pays to parse it. The binary
carries it either way.
Fixed
-
Walkthrough 4 mistook "nothing finalised yet" for a dead node. Asked for
the finalised block before there is one, geth answers with an error
(finalized block not found) and Reth answers with the genesis block. The
walkthrough treated geth's error as silence — with three geth nodes it said
no execution client was answering at all, while all three were. It now tells
a transport failure from an answer that happens to be an error, prints "none
yet" and "genesis" for the two spellings, and says why they differ. -
The walkthroughs are tested. They are what a newcomer meets first, and
nothing ran them. CI now runs 1 and 2 against the lab chain and 3 and 4
against the devnet after finality, and checks each for the conclusion it
exists to reach — "Four calls, one block", "they agree", "one block, from two
implementations" — since a walkthrough printing the wrong verdict still exits
0. It also checks that a devnet walkthrough run against the lab refuses and
names the command that would start the devnet. -
The devnet's execution clients never found each other. Given no
--bootnodes,devp2p discv4 listenbootstraps from mainnet's, so the
devnet's bootnode joined the public discovery network, filled its table with
mainnet nodes, and answered every devnet node's lookup with them —
--netrestrictdiscarded them all, and geth loggedLooking for peers tried=0for ever. A crawl of the bootnode found 136 nodes, one of them on
the devnet. Blocks travel over the consensus network, so nothing visibly
broke; what broke was transaction gossip — a transaction sent to node1 waited
for one of node1's own validators to propose. The bootnode is now given an
explicitly empty list, the mesh forms in seconds,network statusshows
execution peers beside consensus peers and says what an isolated execution
client means, and CI requires every node to see the other two. -
The 3D page drew sixty frames a second for nobody. The browser stops a
background tab by itself, but not a canvas scrolled out of view while the
legend under it is being read — that kept drawing at full rate, which on a
laptop is a fan and a battery. The scene now stops outright when the tab is
hidden or the canvas is off screen (measured in headless Edge: 0 animation
frames in two seconds, against 140–290 in view), and resumes when it comes
back. With reduced motion asked for it draws only when something changes, and
a new block appears rather than gliding in — the glide takes about as long as
a lab block, so it would have kept the scene drawing anyway. The client
markers also stopped spinning under reduced motion; they used to ignore it. -
The control room fetched its own typefaces over the internet. The page
linked fonts.googleapis.com for IBM Plex and Fraunces. It is compiled into a
binary and served from localhost, and this project exists to run a chain on a
laptop with no network — so offline, which is most of the point, every heading
fell back to Georgia and every table to the system sans, with nothing to say
why. Six woff2 files travel with it now, latin only, and a test asserts the
page names no font host and that each file is served asfont/woff2. -
The check that starts a chain from a download tripped over the chain
already running. It stopped the previous step's container and then tried to
bind 8545, which that step's process was still holding — the thing its own
error message sayscupel downcannot help with. It had never run on CI
before, because the commits that added it had not been pushed. -
A downloaded binary could not start a chain. The archive holds a binary,
a README and two licences; every command except--versionwent looking for
compose/lab.ymlin the working directory or a parent, found none, and said
"could not find a Cupel checkout". The point of publishing binaries is that
trying Cupel should not need a toolchain, and what shipped needed a git
clone instead of a Rust one.The binary carries what it reads now — the three compose files, the lab
genesis, and the Prometheus and Grafana configurationcupel observemounts
— and writes them into a directory of its own the first time it runs, named
byCUPEL_HOMEor chosen from where the platform keeps application data. A
checkout still wins wherever there is one, so editing a compose file works
exactly as before. The Engine API secret is deliberately not carried: it is
generated per chain, and one compiled into a release would be the same
secret on every machine that downloaded it.cupel genesisis the one
command that still needs a checkout, because it rebuilds the allocation from
Foundry artifacts, and it now says so instead of failing on a missing path.The release's own smoke test could not have caught this. It runs the binary
inside the checkout, where the file it wants is always a directory up, so it
passed for four releases while every download failed. Two checks replace it:
the release unpacks its own archive somewhere with no checkout above it, and
CI starts a whole chain that way, which is also what proves the mounts in the
carried compose file resolve against the directory it was written to. -
The release never checked the version it was publishing. The step titled
"It runs, and it is the version on the tag" printedcupel --versionand
compared it with nothing. A tag placed on a commit whoseCargo.tomlstill
held the previous number would have published a binary that disagreed with
the release it was attached to. It compares them now and refuses. -
Publishing did not wait for CI.
v0.7.0published while CI on its own
commit was red. Building four binaries proves they compile and run; it says
nothing about the devnet reaching finality or the committed bundle matching
its source. The publish job now waits for the CI run on the tagged commit,
and refuses to publish a commit CI rejected.
Changed
- The workflows use current actions again: checkout v7, setup-node v7,
upload-artifact v7, download-artifact v8 and action-gh-release v3. GitHub was
forcing the old ones onto Node 24 and warning about it on every run.