Repository navigation
feat(shred-txfeed): decode Alpenglow block markers; count them and log the ones that void sent transactions (BLO-48422) - #192
Conversation
…g the ones that void sent transactions (BLO-48422) Mainnet runs Alpenglow since slot 459,653,000 (feature A1pengvu... active at epoch 1064, 2026-10-21). A block is now a BlockHeader marker, entry batches, a BlockFooter marker and a final one-tick batch (SIMD-0307/0337/0384; Agave v4.3.2 entry/src/block_component.rs), each its own DATA_COMPLETE run. A marker opens with a zero entry count, so ParseEntries returned no transactions for it and the feed ignored it silently. That stays safe, but it hid two things: - whether the feed sees whole Alpenglow blocks (a header and a footer each); - the two components that void a slot's earlier transactions after this feed has sent them: an UpdateParent marker (SIMD-0337 fast leader handover; "state changes from transactions before the UpdateParent marker are discarded"; its gate is not active on mainnet yet) and the empty entry batch a leader sends to abandon its block. txfeed.ParseMarker decodes a marker: u64 0, u16 version 1, u8 variant (footer 0, header 1, UpdateParent 2, genesis certificate 3), u16 length, and the body. It returns the parent slot and block id of a header or UpdateParent, and the bank hash, producer clock and client of a footer. It reports an empty entry batch as MarkerAbandon. shred-txfeed counts headers, footers, UpdateParents, abandons and marker errors in its stats line, and logs each UpdateParent and abandon with its slot. No frame, group or subscriber-visible behavior changes. The feed doc now says partition 2 (simple votes) is empty: Alpenglow votes are BLS messages between validators, not transactions.
|
🔗 Paperclip issue: BLO-48422 |
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 82559df
Looks good. I checked the wire layout against Agave v4.3.2 entry/src/block_component.rs and it matches:
- a
u64zero entry count; - a
u16VersionedBlockMarkertag = 1; - a
u8BlockMarkerV1variant (footer 0, header 1, UpdateParent 2, genesis cert 3); - a
u16LengthPrefixedbody; - an inner
u8version for every variant exceptGenesisCertificate; - a footer of
bank_hash, thenu64time, then au8-length user agent.
The abandon case also matches: Agave treats exactly 8 zero bytes as an aborted block.
Verified locally at this head:
go vet ./txfeed ./cmd/shred-txfeed: clean.go test ./txfeed: ok.FuzzParseEntriesfor 20 s, which now also exercisesParseMarker: PASS.
Critical Issues (0)
Important Issues (0)
Suggestions (2)
- [pr-review-toolkit/code]
cmd/shred-txfeed/main.go:397:markers[MarkerGenesisCert]is counted but never printed in the stats line, so a genesis-cert count is collected and then dropped. Either addgenesis_certs=or stop counting it. - [native-codex]
cmd/shred-txfeed/main.go:426: marker detection relies on each marker being its own DATA_COMPLETE run, as the PR body states. If a run ever carried a marker followed by entry batches,ParseEntrieswould still stop at the zero count, and those transactions would be lost without any signal. This behavior predates the PR and is not a blocker. Keep it in mind ifheaders=andfooters=ever come out lower thanslots=.
Strengths
- Bounds-safe decoding throughout. Truncation at every byte offset is tested and returns an error rather than panicking, and
markers[m.Variant]is reachable only for variants 0–3. - The fixture pins the exact 54-byte Agave header prefix, so a drift in the layout fails loudly.
- Subscriber-visible framing is unchanged. The new counters and logs are purely observational.
Recommended Action
- No blocking changes requested.
- Merge once the remaining required CI checks finish green.
…s on live Agave and Firedancer sets (BLO-48422) A Firedancer leader (26.10.x, about 14% of stake) zero-fills every data shred to full size: its header set arrives as the 54-byte marker and then zeros. ParseMarker already ignored bytes past a marker's length prefix, but it called only the exact 8-byte empty batch an abandon, so a zero-filled one would have counted as a marker error. A zero entry count followed only by zeros is now an abandon; no marker is all zeros, since its version is 1. TestAlpenglowLiveSets decodes the header, footer and final-tick sets of two mainnet blocks from a passive capture of the v3 union (232.0.0.2:5001, 2026-10-22 21:41Z): slot 460191482 (Agave 4.3.2, Jito) and slot 460191528 (Firedancer 26.10.4). Reed-Solomon recovers the shreds the capture missed. It checks parent = slot - 1, the bank hash, a producer clock inside the capture minute, the footer client (agave/4.3.2..., and empty for Firedancer), that the final tick is a zero-transaction entry batch, and that only the Firedancer sets are zero-filled. The fixture is the 327 frames, 34 KB gzipped.
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: c6f98a1
Looks good. This is a re-review: there is one new commit since my approval of 82559df, c6f98a1 ("a zero-filled empty batch is an abandon too; test markers on live Alpenglow sets"). The earlier review carried no Critical or Important findings, so there are no prior findings to disposition.
What changed:
ParseMarkernow reads a zero entry count followed by an all-zero tail asMarkerAbandon, so a padded abandon from a Firedancer leader is recognised.- New tests:
TestAlpenglowLiveSetsdecodes the header, footer and final-tick sets of two live mainnet blocks, one from an Agave 4.3.2 leader and one from Firedancer, from a gzipped capture.TestParseMarkergains a 963-byte zero-filled abandon case.
Verified locally at this head:
go vet ./txfeed ./cmd/shred-txfeed: clean.go test ./txfeed: ok.TestAlpenglowLiveSetsandTestParseMarkerboth PASS.FuzzParseEntriesfor 15 s: PASS.
Critical Issues (0)
Important Issues (0)
Suggestions (2)
- [native-codex]
txfeed/solana.go:357: the padded-abandon rule is inferred, not observed. The live fixture contains no abandoned block, and Agave's own rule is exactly eight bytes. The fixture does prove that a Firedancer header marker tolerates a zero tail, so the inference is reasonable, and since abandons only drive a counter and a log line, a misread costs nothing. If a live Firedancer abandon is ever captured, add it to the fixture so this branch is pinned by data rather than by reasoning. - [pr-review-toolkit/code]
cmd/shred-txfeed/main.go:397: carried from the last review and still present.markers[MarkerGenesisCert]is counted but never printed in the stats line. Either addgenesis_certs=or stop counting it.
Strengths
- The abandon check is exact and cannot swallow a real marker: every marker carries version
1in byte 8, so no real marker is all zeros, as the new comment says. - Skipping the truncation loop for abandons is correct, since every zero prefix is itself an abandon.
- The live-capture fixture pins header parent slots, footer user agents, the producer clock window, and whether each leader zero-fills. That catches both a layout drift and a client-behaviour drift.
Recommended Action
- No blocking changes requested.
- Merge once the remaining required CI checks finish green.
Part of BLO-48422 (Alpenglow is live on mainnet; bring the shred pipeline up to date).
Why
Mainnet runs Alpenglow from slot 459,653,000. The feature
A1pengvu…activated at epoch 1064 (2026-10-21T13:36:05Z), and the switch happened at genesis block 459,652,999 (13:54:00Z).Every block is now these components, each its own DATA_COMPLETE run:
Sources: SIMD-0307/0337/0384 and Agave v4.3.2
entry/src/block_component.rs.A marker opens with a zero entry count, so
ParseEntriesreturned no transactions for it and the feed ignored it silently. That stayed safe, but it hid two things:UpdateParentmarker are discarded". Its gate is not active on mainnet yet; Agave 4.4 is expected to bring it.What
txfeed.ParseMarkerdecodes the wire layout:u64 0, thenu16version 1, thenu8variant (footer 0, header 1, UpdateParent 2, genesis cert 3), then au16length, then the body. It returns:agave/4.3.2) of a footer.An 8-byte empty batch comes back as
MarkerAbandon.shred-txfeed:headers=,footers=,update_parents=,abandoned=andmarker_err=. Nothing parses that line, as checked in go-amt and onprem-k8s.Tests
TestParseMarkercovers:00×8 01 00 01 29 00 01that agave v4.3.2 writes;FuzzParseEntriesnow also runsParseMarkeron every input. A 20 s local fuzz passed (231k execs).go vetandgo test ./txfeed/ ./cmd/shred-txfeed/pass.Verified on live bytes
TestAlpenglowLiveSetsdecodes the header, footer and final-tick sets of two mainnet blocks. They come from a passive capture of the v3 union (232.0.0.2:5001, 2026-10-22 21:41Z), and Reed-Solomon recovers the shreds the capture missed. The two blocks:For both blocks the test confirms:
slot − 1;agave/4.3.2 …, or empty for Firedancer;Firedancer zero-fills every data shred to full size. Its header set is the 54-byte marker followed by zeros.
ParseMarkerignores bytes past the marker's length prefix. It also treats a zero entry count followed only by zeros as an abandon, because a zero-filled empty batch would otherwise count as a marker error. No marker is all zeros, since its version is 1. The fixture is 327 frames, 34 KB gzipped.Deploy
shred-txfeedruns as a CT 140 host unit, and rolling it there is Omar's go.