explorer: fix /blocks per-element state and adj; consensus: pbft cfg checksum - #2079
Open
Maxnflaxl wants to merge 2 commits into
Open
explorer: fix /blocks per-element state and adj; consensus: pbft cfg checksum#2079Maxnflaxl wants to merge 2 commits into
Maxnflaxl wants to merge 2 commits into
Conversation
Maxnflaxl
force-pushed
the
issue/2077-2078
branch
from
August 18, 2026 09:56
9570cb1 to
f5e404c
Compare
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
Two bugs in the explorer's
/blocks(plural) endpoint, reported by @dbadol as #2077 and #2078. Both live inAdapter::get_blocks, so they're fixed together.Plus an unrelated one-line
Rulesfix, found while investigating why @dbadol's dappnet2 explorer wouldn't start on a post-HF6 build — see the third section.#2077 — every element carries the first block's header
Regression from ba181a9, which replaced the old per-row
get_block_impl()walk with aStateID/m_Numberloop. The loop advances the row but never reloads the state for it, so every element after the first is rendered against the first block'sBlock::SystemState::Full:sid.m_Number.v++; sid.m_Row = _nodeBackend.FindActiveAtStrict(sid.m_Number); // `s` left staleBecause the body is read from
sid.m_Row(correct) while the header and context come froms(stale), the damage is wider than the report: on top ofhandinfo, thetotalstable (Chainwork,Current Emission,Current Circulation,Size Compressed,Size Archive) andoutputs[].Maturityare all wrong. Verified against mainnet — for block 27730 in aheight=27729&n=4series, emission reads2,218,320instead of2,218,400and the coinbase maturity27969instead of27970.inputsandkernelswere the only correct sections.Fixed by reloading the state after each step.
#2078 —
adjis ignoredadjsimply never reached the backend:on_request_blocksparsed onlyheightandn, andget_blockshad noadjparameter. What looked like "defaults toadj=1" wasFindBlockByHeight→FindAtivePastHeightsnapping to the next active height unconditionally, which also meant/blockshad no not-found path at all.Fixed per the three points in the issue:
adjis parsed and passed through, and applies to the first block of the series only; the rest always follow upwards.heightwith noadjreturns{"found":false,"height":N}, matching/block.nentries.To keep
/blockand/blocksfrom drifting apart again, the first-block resolution is now shared:get_block's logic is factored intoResolveBlock(sid, s, h, height, adj)and both endpoints call it.Rules— PBFT networks can't start after HF6Separate from the above, and the reason the
/blocksfixes can't be tested on dappnet2 right now: any build after 3c1e73f ("consensus: fees go into coinbase from HF6") refuses to start on dappnet2 and warp_dev3 withThat commit added
<< (uint32_t) 1 // fees in coinbaseto the fork6 oracle, changing the HF6 hash. Both networks useSetForksFrom(0, 0), so all their forks sit at height 0 and their DBs are stamped with the HF6 hash — whichFindFork()no longer finds, soNodeProcessor::Initializethrows. mainnet/dappnet/testnet/masternet are unaffected: they stamp HF5, which didn't change.The rule itself never applies to PBFT — both the validation (
assert(Rules::Consensus::Pbft != rules.m_Consensus)inblock_validation.cpp) and the block assembly are gated on non-PBFT. Only the checksum line wasn't, so the PBFT nets break over a rule that can't fire on them. Gating it matches the existing conditionals inUpdateChecksum(CA.ForeignEnd, theShielded.MaxIns/MaxOutspair, the whole PBFT block).Fork tables dumped per network, before and after:
c94b179468bd193f28c7c7b263b6254a(the hash in @dbadol's DB)1a430a4062b84b5de2fc30342d1b6d4c96df3f33ee02ad9e96df3f33ee02ad9e— unchangedSo both PBFT nets start again on their existing DBs, with no resync and no coordinated upgrade, and mainnet — already past HF6 at 3,928,666 — is untouched. Chain data is unaffected either way, since the rule never applied to those chains.
Changes
explorer/server.cpp—on_request_blocksparsesadjand passes it on.explorer/adapter.h—get_blocks(Height, uint64_t, int adj).explorer/adapter.cpp— newResolveBlockhelper shared withget_block;get_blocksrefreshes the state per element, honorsadjfor the first block, and emits not-found placeholders.core/block_crypt.cpp— the fees-in-coinbase value is fed to the fork6 checksum for non-PBFT consensus only.Behavior
blocks?height=2743&n=4blocks?height=2742&n=4(vacant)[{"found":false,"height":2742}]blocks?height=2742&n=4&adj=1blocks?height=2744&n=4&adj=-1nentries, tail isfound:falseTwo judgement calls worth a look, since the spec is ambiguous for PoS chains:
/blockstype-stable for clients. Easy to change if the bare object is preferred.tip+1,tip+2, …), which only carries meaning on PoW. On a PoS chain with gaps they should be read as "no block here", not as a height prediction.Closes #2077
Closes #2078