explorer: restore treasury view by treating explicit height=0 distinctly from a missing height#2064
Merged
Merged
Conversation
#1990 made `block` with no arguments return the current tip instead of the treasury, but implemented "no arguments" as `if (height)`, which treats an absent height and an explicit height=0 identically. That silently broke the treasury view (`?type=treasury` requests `block?height=0`), which then rendered the latest block. Root cause: Height is uint64_t with 0 as a valid height (the treasury pseudo-block), yet 0 is also the "unspecified" sentinel. get_int_arg("height", 0) collapses an absent arg onto the same 0 as an explicit &height=0, so the two become indistinguishable before get_block ever sees them. Fix: distinguish the cases in Server::on_request_block, underneath get_int_arg, by checking the raw args map (the one layer that still knows presence): - height absent -> latest block (keeps #1990's intent) - explicit height=0 -> treasury (restores the frontend link, no HTML change) Add get_treasury() to IAdapter so the router can dispatch to it directly, and annotate the fallback in get_block() as an edge-case guard.
valdok
approved these changes
Jul 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The treasury view (
?network=mainnet&type=treasury, which requestsblock?height=0) was silently broken: it displayed the current tip block instead of the treasury UTXO groups.The cause is #1990 ("display current block instead of treasury when calling block with no arguments"). That change implemented "no arguments" as
if (height) { … } else { return tip }, which treats an absentheightand an explicitheight=0identically. As a result, the only route that reachesget_treasury()on a synced full node was removed, and the frontend's treasury link — which deliberately sendsheight=0— started rendering the latest block.The
get_treasury()fallback inget_block()was noted at the time as "kept for safety … in practice never reached under normal operation." That was accurate, but it was the symptom: the code path had a real caller (type=treasury→height=0), so making it unreachable broke a feature rather than pruning dead code.Root cause
"No argument" and "
height=0" were never actually distinct in this code — the ambiguity is structural, in two layers:Heightisuint64_tand 0 is a valid height (the treasury pseudo-block), yet 0 is also the "unspecified" sentinel. The value meaning "give me the zero-height block" is the same value meaning "caller gave nothing."get_int_arg("height", 0)erases the distinction that still existed in the raw request. Theargsmap knows whetherheightwas in the URL, butget_int_argreturns the default0for an absent arg — the identicalint64_tproduced by an explicit&height=0. From that call onward the two are physically indistinguishable, andget_block'sif (height)reads both as "no height."So it wasn't a deliberate decision to equate them — the code simply never carried a "was it provided?" bit, and reused
0for two meanings. That's why the fix has to reach underneathget_int_arg.Fix
Distinguish the two cases at the router level, in
Server::on_request_block, by checking the rawargsmap (the one layer that still preserves presence-vs-absence):heightabsent → latest block (preserves Explorer node: Display current block instead of Treasury when using "block" with no arguments #1990's intent; bareblockstill shows the tip)height=0→ treasury (restores the existing frontend link, no HTML change)A
get_treasury()method is added to theIAdapterinterface so the router can dispatch to it directly, and the existing fallback insideget_block()is annotated as a genuine edge-case guard (fresh node with no blocks, or prune-navigation viaadj<0).Changes
explorer/server.cpp— inon_request_block, route an explicitheight=0toget_treasury(); a missingheightstill falls through toget_block().explorer/adapter.h— addvirtual json get_treasury() = 0;toIAdapter.explorer/adapter.cpp— exposeget_treasury()(wraps the existing privateget_treasury(json&)); document the in-get_blockfallback as an edge-case guard.Behavior
block(no height)block?height=0block?height=N?type=treasury(frontend)No frontend change required. Compiles clean (
explorertarget).Testing
Tested the above behaviors, all good.