Repository navigation
chore: merge dev-upgrade into main - #2903
Merged
Merged
Conversation
…ata (#2719) The UnpackError method generated by abigen --v2 indexes raw[:4] without checking the length first, so revert data shorter than 4 bytes (e.g. an empty revert) makes the call panic instead of returning "Unknown error". Return the unknown-error result early when the data is too short to hold a selector, and regenerate the committed v2 bindings that carry custom errors (solc_errors) so they stay in sync with the template. Upstream go-ethereum has the same pattern (its source2.go.tpl still indexes raw[:4] after 4a305af) and has no fix for it, so this change is made independently without referencing an upstream PR.
…che (#2722) * test(accounts/abi/bind): pin the MetaData ABI cache MetaData.GetAbi backs the abigen v1 binding constructors: every New<Contract>Caller, New<Contract>Transactor and New<Contract>Filterer call it, so the parsed ABI is supposed to be computed once and handed out from the cache afterwards. Those call sites come from the abigen v1 template and reach this tree with the regenerated v1 bindings (#2784); until that lands the cached path has no caller here and only the test below exercises it. Nothing in this package pinned that down, as it carried no test file, so a bypassed cache, a cached parse failure, a cache shared between bindings and an unguarded concurrent first call would all have gone unnoticed. Four cases cover those four halves of the contract. The first calls GetAbi twice and requires the same *abi.ABI back, and also checks the parse result, so that a cache returning one shared error or one zero value cannot pass. The second requires a rejected ABI to report an error, to return no ABI, to leave the cache empty, and to parse successfully once the ABI is corrected. The third gives two bindings different ABIs and requires each of them to keep its own parsed result, and then gives a third binding the first ABI again to require that two instances carrying the same ABI still get one parsed result each. The fourth releases 32 goroutines at once onto an empty cache, over 16 rounds, and requires all of them to get the same instance back. Every case was checked for discriminating power by mutating GetAbi through go test -overlay: dropping the early return of the cached pointer fails the first and fourth cases, caching an empty ABI on a failed parse fails the second, and handing out a package-level singleton while still reporting parse errors fails the second and third. Dropping the mutex fails the fourth under the race detector and, without the race detector, in the large majority of runs, because the pointer check depends on how the 32 callers interleave. A package-level cache keyed on the ABI string instead of on the instance fails the third case as well, at the same-ABI assertion. No production code changes. gofmt, goimports, go vet and go test -race pass on this package, and make all and make quick-test pass for the tree. * test(accounts/abi/bind/v2): pin the MetaData ABI cache MetaData.ParseABI is the v2 counterpart of the v1 GetAbi pinned in the previous commit, and the two are the same contract line for line: hold the mutex, hand out the cached *abi.ABI once parsed, and leave the cache empty when the ABI does not parse. Generated v2 bindings keep their MetaData in a package-level variable and call this method from every New<Contract> constructor, the instance wrappers then reusing the parsed ABI it caches, so the same four regressions are possible here: a bypassed cache, a cached parse failure, a cache shared between bindings and an unguarded concurrent first call. The package had no test for any of it. The four cases mirror the v1 ones, with the second ABI carrying another method name so that a cache ignoring its receiver cannot pass, and the third also handing a third binding the first ABI again so that two instances carrying the same ABI must still parse one result each. Discriminating power was checked by mutating ParseABI through go test -overlay: dropping the early return fails the first and fourth cases, caching an empty ABI on a failed parse fails the second, handing out a package-level singleton while still reporting parse errors fails the second and third, and dropping the mutex fails the fourth under the race detector. A package-level cache keyed on the ABI string instead of on the instance fails the third case as well, at the same-ABI assertion. No production code changes. gofmt, goimports, go vet and go test -race pass on accounts/abi/bind/v2, and make all and make quick-test pass for the tree.
* fix(internal/ethapi): preserve access lists with gasPrice * test(ethapi): document access-list regression cases * test(internal/ethapi): align access-list regression coverage Move the access-list regression next to the existing FillTransaction tests. Use the same parallel setup, explicit sender and AddrLocker, and verify the transaction type, gasPrice and access list after decoding the returned raw transaction. Drop the duplicate gasPrice-only control; the existing TestFillTransactionLegacyWhenGasPriceProvided already covers that case. The production fix remains a deliberate local divergence from inherited geth behavior. No upstream fix was identified at the reviewed geth snapshot f8f9bc5. The inherited comment was "Make it possible to default to newer tx, but use legacy if gasprice is provided". Here an explicit access list must survive the gasPrice override. Keep the explicit legacy/access-list selection. The shorter guard does not cover the CallDefaults/ToTransaction path used by eth_simulateV1. CallDefaults can fill zero dynamic-fee fields alongside gasPrice; these and the dynamic default select type 2 before the access-list branch. Set-code defaults or an authorizationList also take precedence. A local diagnostic passes five call-conversion cases with the existing override; the shorter guard fails the four access-list cases while preserving the no-access-list control. These are not all valid FillTransaction inputs, and the diagnostic is kept outside the patch. Do not add a fee-style change to this narrow correction. --------- Co-authored-by: Supersnips <48771887+SupersnipsXDC@users.noreply.github.com>
#2714) * fix(eth/filters): restore pending filter hash responses * test(filters): document pending-filter response regressions * test(eth/filters): document nil api.sys precondition Explain why the hash-only pending tests do not need a filter backend, and when they must use newTestFilterSystem. No executable code changes. --------- Co-authored-by: Supersnips <48771887+SupersnipsXDC@users.noreply.github.com>
ported from go-ethereum ethereum#32506 InspectDatabase walked the keyspace and never consulted it.Error(), so a walk that the database aborted printed a table built from a partial scan and returned nil, which reads as "inspection passed" to whoever ran db inspect. Only the per-key accounting was affected; nothing on the consensus or sync paths reads this function, its single caller is the db inspect command. Upstream landed the check as part of parallelising the traversal, so its InspectDatabase is a worker closure over per-range stat structs driven by an errgroup. The version here is the single-threaded one and the two do not line up line for line, so only the semantics are ported: report the pending iterator error once the walk ends. The defect predates dev-upgrade, hence a separate commit rather than a fixup. The test also pins the user-visible half of the early return, by capturing os.Stdout around the call: an aborted walk must render nothing, and a clean walk over the same fixture must still render the report, down to its per-category rows and a non-zero total. Upstream orders it the same way - its workers return the iterator error and eg.Wait short-circuits before the table is rendered - but upstream has no test for either function, so the assertion is ours. One direction alone cannot tell an early return apart from a function that never renders anything. Confirmed with go test -overlay, both directions: deleting the check fails the first assertion with got <nil>, moving it below table.Render() fails the second one on the table built from the partial scan. The capture helper drains the pipe from its own goroutine and restores os.Stdout through t.Cleanup, so the walk cannot deadlock against the pipe buffer. The aborted-walk fixture is shared with TestDeleteDanglingHashesIteratorError, which landed with #2707; it lives in accessors_chain_test.go, so this test keeps no copy of its own. The zero-total assertion counts the total rows it recognises and fails when it finds none, so a column layout it no longer matches cannot leave the check silently vacuous.
…n (pre-existing) (#2726) MakeChain wrote the --cache-gc percentage into cache.TrieCleanLimit, one statement after --cache-trie had written the same field, so on the MakeChain path (XDC import/export and the MakeChain tests) --cache-trie was silently ignored and --cache-gc ended up sizing the clean trie cache. SetEthConfig maps the same flags the other way round: --cache-trie sizes the clean cache and --cache-gc the dirty one, which is also what the flag help texts describe, so MakeChain now agrees with it. The line goes back to the ethereum#18087 port (#946, b9a6c8c): that commit split the old cache.TrieNodeLimit into TrieCleanLimit and TrieDirtyLimit, pointed the dirty flush limit at TrieDirtyLimit in core, and rewrote this very statement, which had to become TrieDirtyLimit, as TrieCleanLimit. Upstream ethereum#18087 (434dd5b) rewrote the same statement to TrieDirtyLimit, so there is no upstream fix to port: --cache.gc has always sized the dirty limit there. The cache assembly moves into makeCacheConfig to make the mapping testable. With --cache 20088 --cache-trie 20 --cache-gc 5 the clean limit is 4017 MB (instead of 1004 MB) and the dirty limit 1004 MB (instead of the 256 MB default). The mapping is pinned by TestMakeCacheConfigSplitsTrieCachesByFlag (defaults, the split, either flag on its own, --cache on its own, archive mode and prefetch) and the GC mode guard by TestMakeCacheConfigRejectsInvalidGCMode. The --cache-trie and --cache-gc help text parentheticals are corrected in the same commit: ac2f09f (#2501) rebalanced the values when the total allowance went from 1024 to 3072 MB (cache-trie 15 -> 5, cache-gc 25 -> 34) but left the Usage strings advertising the old defaults, so --help showed percentages the flags no longer had. The archive-mode parentheticals are inherited from go-ethereum, where they are equally static, so they are left untouched to stay upstream-faithful.
When insertChain cannot open the parent state for the followup-block prefetch, NewWithChainConfig returns a nil StateDB, and the prefetch carried that nil straight into its goroutine, where IntermediateRoot dereferenced it. A panic in a goroutine cannot be recovered, so the node died instead of importing the block without prefetching - the crash reported as #1738. The hazard is pre-existing rather than introduced here: the discarded error came in with the concurrent prefetch itself, and the earlier fix for #1738 (#1742) removed a variable-capture race in the same block but left the error discarded, so a nil still reached the prefetcher whenever the parent state could not be opened. Prefetching is best effort, so the prefetch now starts only when the state opens, and Prefetch returns early when it is handed a nil state; no caller can take the node down this way. CacheConfig.TrieCleanPrefetch is fed by the --cache-prefetch flag, either directly (makeCacheConfig in cmd/utils/flags.go) or through eth.Config.Prefetch, which SetEthConfig writes from that same flag (eth/backend.go); the flag has no default value, so the default behaviour is unchanged. A [Eth] Prefetch key in a config file is not an effective input, because SetEthConfig overwrites it. go-ethereum cleared the same hazard when it rewrote the prefetcher for in-block prefetching (ethereum#31557): it creates the throwaway state with a checked error and never hands nil to the goroutine. This fork still carries the older shape, so this is a local guard rather than a port. TestStatePrefetcherToleratesNilStateDB calls Prefetch with a nil state and reproduces the #1738 stack without the guard, so the test pins the fix.
…2708 (#2740) * fix(core): skip the masternode refresh when the engine is not XDPoS UpdateM1At reports ErrNotXDPoS when the config names XDPoS while the engine is not *XDPoS.XDPoS, and both of its callers turned any error into log.Crit, i.e. os.Exit(1): the head refresh inside adoptHead and the reorg apply loop. A chain built through the exported NewBlockChain and SetChainConfig with an XDPoS config and another engine therefore died on its first gap block. A masternode set has no meaning without the XDPoS engine, so both sites now skip the refresh on ErrNotXDPoS and keep halting on every other failure, which is the contract the head-refresh site documents. The defect is pre-existing: the adoptHead shape came in with the #2566 line and the reorg site is as old as the reorg refactor, so this is a new commit on top of dev-upgrade rather than a fixup. go-ethereum has no XDPoS engine and no masternode refresh, so there is nothing to port. Reachability: CreateConsensusEngine derives the engine from the same chainConfig it hands to the chain, so a normally started node cannot build the pair and nothing changes on mainnet, Apothem or devnet. The exported NewBlockChain plus SetChainConfig can, which is the combination the issue reports. * fix(internal/ethapi): return no signed block when the engine is not XDPoS findNearestSignedBlock asserted api.b.Engine() to *XDPoS.XDPoS inline, so a chain whose config names XDPoS while its engine is not XDPoS panicked there instead of reporting that it has no checkpoint to find. Only the XDPoS engine has epoch switches, so there is nothing for another engine to find; the helper now returns no signed block, which its caller findFinalityOfBlock already answers as finality 0. Nothing changes for a chain whose config and engine agree, which is every chain a normally started node builds. The site is reachable through the exported core.NewBlockChain plus SetChainConfig: CreateConsensusEngine derives the engine from the same config and cannot build the pair. go-ethereum has no counterpart - it has no XDPoS engine and no epoch-switch query - so there is nothing to port. * fix(ethclient/simulated): answer instead of asserting the XDPoS engine The finalized-block branch of the simulated filter backend asserted fb.bc.Engine() to *XDPoS.XDPoS after checking the config alone, so a chain whose config names XDPoS v2 while its engine is not XDPoS would panic there. Naming XDPoS v2 in the config does not make the engine XDPoS, and the engine is what holds the committed block info; the branch now answers the same error it already returns for a v1 chain. The site is not reachable today: New routes a config that names XDPoS to newXDCSimulatedBackend, whose engine is XDPoS.NewFaker, so the pair cannot be built through it. It is guarded for consistency with the invariant the issue tracks rather than as a live defect. go-ethereum has no counterpart, so there is nothing to port. * fix(core): fix pre-existing method-value block number in the gap refresh log refreshMasternodesForGapBlock logs block.Number, a method value, in its crit field, so the number it reports is a function address. The two inline crit sites the helper replaced already read that way, so the slip predates this branch; the warn field the branch added is fixed in the commit that adds it. The helper is XDC-specific and has no upstream counterpart, so there is no geth PR to reference.
…reum#21414 (#2749) XDC import built a full node first, which opened chaindata, and then opened the same database a second time through utils.MakeChain. LevelDB takes an exclusive lock on the database directory, so the second, writable open failed and the command exited before reading the file it was given, on every datadir and for every input file: Fatal: Could not open database: resource temporarily unavailable Build a config node instead, the way export, dump and the other chain commands already do, so that MakeChain is the only opener. That also collapses the duplicate SetupMetrics call that makeFullNode followed by importChain made. Skipping makeFullNode also leaves out the XDCx service registration, the xdc/info gauge and the ethstats service it used to set up. None of them changes what this command does - the import chain comes from MakeChain's own engine, and the command never starts the node (stack.Start() is never called). One consequence is visible: this fork's RegisterEthService is where --syncmode light is rejected ("light mode has been deprecated"), so a light-mode import used to fatal before it read the database and now runs the import instead. Upstream's MakeChain takes only the stack, so it can discard the config node's result; here the chain config mismatch policy comes from the same config, so the bound cfg has to be kept. Upstream's ethereum#21414 shipped no test either and nothing upstream covers this command's database open, so no regression test comes with this change. It was verified by hand on this branch's binary, against two throwaway datadirs: XDC --datadir A init testnet XDC --datadir A --testnet export chain.rlp # 669 bytes, genesis only XDC --datadir B --testnet import chain.rlp # exit 0 where chaindata is opened once (521.00MiB) and "Importing blockchain" is reached. Restoring makeFullNode on the line above and rebuilding reproduces the original failure on the same three steps - XDCx 128.00MiB, then chaindata 768.00MiB from the eth service, then chaindata 521.00MiB from MakeChain - and then: Fatal: Could not open database: resource temporarily unavailable # exit 1 The pull request records a replay of a 152-block export on a private network, which is what covers imported data rather than the open itself. Ref: ethereum#21414
…rds (#2748) The marker #2566 introduced is written for every block this node executes and nothing evicted it: one key per executed block, ~0.9 GB on a chain of 107.5 M blocks. Give it the same lifetime as the state it guards. HasExecutedBlock is a conjunction, and below head-TriesInMemory the second half decides it: the write path drops the state of those heights from memory instead of writing it, so on the heights whose root no longer resolves the marker is not an answer there. The eviction therefore rides the trie garbage collector's own cursor in writeBlockWithState, next to the Dereference that ends the state: no sweep, no cursor, no throttle and no background goroutine, and nothing is scanned. What tells the two lifetimes apart is the state itself, asked of the trie database after the dereference rather than assumed from the height. A root a flush committed - the one above the loop, an earlier one, or a Cap - stays readable on disk, and a root another queued height still references stays readable in memory, so on those heights HasBlockAndFullState rests on the same read the probe makes, apart from the TIPXDCX conjuncts, and the marker is the half that decides. They are not one height per call: a height whose header names a root the flush wrote - a block without transactions and without rewards - is readable for the same reason, and dereferencing a committed root takes nothing off disk for it. Probing is what makes an absent marker unable to change any answer, for any caller, on every height rather than only on the one the cursor happens to stand on. The queue has a second exit, and it takes no markers with it while the head sits at the last block the write path executed: saveData drains, at shutdown, what the write path had not reached yet, but every height still queued then sits inside the state window - the write path pops every entry at or below its cursor before it breaks - and there the marker still decides - over the state saveData has just committed to disk on head, head-1 and head-(TriesInMemory-1), and, on the rest, over a root that is readable on disk - one of those three, one a chain of blocks without transactions shares with them, or one a Cap left there. Taking those markers would turn "this node ran the block" into an execution for every height whose state outlives the restart, so the drain dereferences the roots and carries the cursor as a guard rather than a cut: the write path pops every entry at or below it, so no queued height can reach the delete and the drain takes no marker on that line. A head adopted past the last written block - writeKnownBlock moves the head without a write - can leave queued entries below the drain's line, and that arm takes their markers with the roots it dereferences, cutting by its cursor instead of asking the state: the one place the two lifetimes can part company. It is recorded in the ADR rather than solved here. One window of markers is left behind per clean shutdown, and nothing later takes them. An archive node never dereferences state, so there the marker decides all the way down and every marker is kept. Evicting there is what would need a window, a persisted cursor and a pass over the receipts key range, and it would make a range an archive node still holds the state for be executed again rather than recognised. Markers a database already carries are not swept, because the eviction names only the heights the queue still holds: an upgraded node bounds its growth from here on rather than reclaiming what is behind it, and the inert keys below the line go only when SetHead or a re-import rewrites those heights. bc.triegc carries the block hash alongside the state root so the eviction can name the marker's key without a read, and DeleteExecutedMarker now returns its refusal: DeleteReceipts and the rewind keep treating it as fatal, while the two garbage-collector exits report and skip it - behind the cursor, so the cost is one stale marker and not the block. Decision and rejected alternatives: docs/adr/0001-bound-executed-marker-to-state-lifetime.md
…ereum#20053 (#2751) The attach tests slept a fixed two seconds before dialling the freshly started node. Under a parallel test run, node startup (opening the two leveldb caches, genesis setup, the blockchain recovery check and the txpool constructors) can take longer than that, so the attach raced the listener and failed with "connection refused". Poll the endpoint with rpc_modules until it answers, with a two minute budget, the same way geth does it in cmd/geth/run_test.go. Each attach test now finishes as soon as the endpoint is up (about 0.2s) instead of always burning two seconds. The poll can now fail before the attach is even attempted, and t.Fatal would skip the trailing Interrupt/ExpectExit pair, leaving the node running. The three tests therefore register t.Cleanup(XDC.Kill), which kills the node on that path too. Ref: ethereum#20053
Add the JetBrains go-modern-guidelines skill to .agents/skills/use-modern-go so it is available as a project-level skill in this repository. - Invoke via the use-modern-go skill (slash command or use_skill). - Guidelines are checked against a target Go version with scripts/run-tool.sh list --go-version <ver>. - .agents/ is globally gitignored, so the directory is force-added (git add -f); it stays tracked thereafter. Tooling only; no runtime/source changes.
Amsterdam is the fork upstream go-ethereum gives the EIPs that did not land in Osaka, and this chain has no switch to hang them on: params.ChainConfig stops at OsakaBlock and nothing selects amsterdamInstructionSet, which core/vm/jump_table.go builds but no fork ever reaches, so the table built by EIP-8024 is dead code. Add AmsterdamBlock as a block-number fork, the way every other fork here is expressed, together with IsAmsterdam, Rules.IsAmsterdam and the fork order entry that keeps it after Osaka, and select the table from NewEVM and LookupInstructionSet. Amsterdam is Osaka plus the rules that did not land there, so scheduling it without Osaka would execute rules the chain never switched on. The linear order check cannot catch that on its own: it compares two forks only when both are set. CheckConfigForkOrder gains a fork-order special case that requires Osaka to be set before Amsterdam, and the case table pins it with an amsterdam-without-osaka case beside the amsterdam-before-osaka one. No live network schedules it: XDCMainnetChainConfig, MainnetChainConfig, TestnetChainConfig, DevnetChainConfig and LocalnetChainConfig leave amsterdamBlock unset, and Osaka is unset on all of them, so Amsterdam cannot be scheduled first. The dev and test configs that run every fork from block zero schedule Amsterdam with Osaka. The table stays Osaka plus EIP-8024. EIP-7843 needs a slot number this chain has no source for, and EIP-8037 and EIP-8038 replace the single-value gas ledger with a two-dimensional one, so enabling them means porting that ledger first. newAmsterdamInstructionSet records that they are missing. Bump chainConfigDigestVersion: the canonical encoding covers one more fork field, and the version byte is part of it, so the digest moves and the pinned vectors in core/chainconfig_equal_test.go are re-pinned with it. The digest is only ever computed and compared in-process, so no cross-version fallback is involved. Ref: #2730
…reum#33832 ethereum#35217 (#2757) EIP-7954 raises the contract code and initcode size limits, and upstream go-ethereum has always gated it on Amsterdam: MaxCodeSizeAmsterdam arrived with the EIP itself, and ethereum#35217 later raised it from 32768 to 65536 to match the EIP as it stands. The port here hung the limit on Osaka instead and kept the first value, so Osaka on this chain would have carried a rule upstream Osaka does not have, at half the size upstream Amsterdam allows now, and Amsterdam had nothing to carry when it was finally added. Gate the limit on IsAmsterdam and adopt the current upstream values, 65536 of code and 131072 of initcode. Osaka falls back to the 24576 and 49152 it has upstream, which is what makes the two Osakas equivalent again. No network schedules either fork, so no chain changes behaviour today. The dev and test configs that run every fork from block zero run Amsterdam, so the oversized-initcode case in createGasTests moves past the new limit: a create2 has to exceed 131072 now, not 65536, to be rejected. The fixture in core/state_processor_test.go runs Osaka without Amsterdam, so its too-big initcode comes from params.MaxInitCodeSize, the 49152 those rules allow, and the conditional that used to hand it the Osaka limit goes away. Ref: ethereum#33832 ethereum#35217
#2758) * docs(upgrade): normalise the wrapping and structure of the upgrade notes The file mixed hard-wrapped paragraphs with single-line ones, so editing it meant re-wrapping by hand, and every later change to a wrapped paragraph showed up as a diff across the lines it happened to span. Put every paragraph, bullet and numbered item on its own line; 187 continuation lines are joined. That same pass restores structure the wrapping had flattened. Nine bullets in the presence-tracking list carried their four labelled sub-items as indented text on the bullet's own line, and the same shape appears in three groups further down, where each parent is a plain paragraph rather than a list item; those labelled lines are now a list under it, without a markdown-level nesting. The six steps of the chain-config upgrade procedure, which the wrapping had run together into one paragraph around two commands, are a list, and the sentence that introduces the second command stays plain text inside the item it belongs to rather than becoming an item of its own. The runtime choices for the four networks named each network twice, once for its flag and once for its notes; each network is one bullet now, with its startup flag and its notes as sub-items. Existing fenced blocks, tables and list indentation are untouched. This is not a whitespace-only change: the list markers are new. * docs(solidity): rewrite the gas price notes against block.basefee The minimum gas price the pool accepts is the gas price of the block's tier, the same value `block.basefee` reports, and the two were documented a section apart: the special variable under `## Special variables`, the pool floor in a top-level `## Gas price` section of the same file. Say it once, in the section that already explains what the tier is. The floor sentence, the per-network table and the `eth_gasPrice` advice move under `### block.basefee`, and the standalone section goes away. The sentence also records that transactions to the block-signer and randomize system contracts are exempt from the floor, and the file loses the blank line the removed section had left at the end. The two paragraphs this commit edits in place, the pool-acceptance sentence and the `blobbasefee` note, also move to one line per paragraph. The rest of the rewrap stays with the commit that documents the Amsterdam evm version. * docs(solidity): rewrap the notes and document the amsterdam evm version Amsterdam is a fork this chain now has a switch for and a target the compiler started exposing in 0.8.36, but the compatibility table stops at Osaka and never mentions it, so a reader cannot tell whether `--evm-version amsterdam` artifacts work here. They do not: the fork is not implemented (EIP-7843, EIP-8037 and EIP-8038 are missing) and no network has scheduled it. Add an Amsterdam column that is TBD on every network, and a note recording that 0.8.36 began exposing the target and 0.8.37 added `block.slotnum` and `slotnum()`, both only with `--evm-version amsterdam`, still experimental and not the compiler default. The code and initcode limits EIP-7954 raises belong to Amsterdam rather than Osaka, so the size-limit section states them too. The note says the switch exists while the instruction set does not cover the target, because at that point the evm version is reachable in the table but not in the compiler. The file also moves to one line per paragraph, matching the format rule the rest of the change follows; the only wording that pass touches is note 3, which now points at note 7. * docs(solidity): document the prague eip-2935 history contract The upgrade notes describe where the EIP-2935 history contract is deployed and how it is backfilled, which is what an operator needs. What a contract can do with it was not written down anywhere: `docs/solidity.md` documents the special variables a contract reads, and the history contract is the way to read a block hash older than the 256 `blockhash` answers for. Add that half. `blockhash` is unchanged, the contract keeps the last 8191 hashes, reading one means calling it with the block number, and a number outside that window reverts rather than returning zero. The deployment and backfill rules stay in `docs/upgrade.md`, which this commit does not touch, so the two files split by audience rather than duplicating each other. * docs(upgrade): correct the eip-2935 deployment and backfill notes The section described a backfill of recent parent hashes at upgrade time, which neither the code nor EIP-2935 does: block processing deploys the contract at the activation block, writes that block's parent hash into one slot, and every later block adds one more, so the rest of the window reads back as zero until 8,191 blocks have passed. Document that route together with the upstream one, where the account is created by the presigned deployment transaction the EIP specifies and clients carry no deployment code of their own, and record the two further local deviations in the failure handling and the gas budget. * docs(solidity): drop the stale prague premise from note 5 The note still asked the reader to imagine Prague activating "in the future", while the activation table in the same file has carried devnet at block 50000 and the Prague section added alongside it describes what is already scheduled. * docs(solidity): add the testnet gas2500x tier to the base fee table The table named one tier fork per network, but testnet also has Gas2500xBlock set at 83600000, where the floor moves from 12.5 GWei to 625 GWei. The prose already described that transition; the table an operator looks up did not. * docs(upgrade): generalise the fork declaration requirement The section named Prague alone, but osakaBlock and amsterdamBlock join the same rule: both are part of the built-in backfill set and are not inherited by custom-network hydration, so they need the same explicit declaration. Naming the backfill-set forks explicitly keeps the rule readable as forks are added. * docs(solidity,upgrade): fix pre-existing spelling and quotation slips Both slips predate this branch. `implemention` in the `block.prevrandao` and `block.blobbasefee` notes is present at the base commit (lines 61 and 85 there), and the "semantically different" sentence in the upgrade notes already used curly quotes (line 203 there). The gas-price and rewrap commits in this branch reproduced both verbatim, so they arrive unchanged. Fix the spelling and match the straight quotes used everywhere else in both files; no other wording changes. * docs(solidity): align the activation table with the scheduled testnet prague #2811 scheduled Prague on testnet at 87633000, so the TBD in that cell is stale, and the TBD convention itself needs stating: with mainnet unset and testnet scheduled, TBD can only mean that no activation block is scheduled. Title the table accordingly and widen the Prague column to hold the eight-digit block. The precompile note repeats the stale premise in its Apothem clause, and three sentences claimed "no network" / "every network configuration" / "everywhere" while the dev and test fixtures schedule Amsterdam and Osaka from genesis. All now say "real network". Ref: #2811 * docs(solidity,upgrade): fix pre-existing inconsistencies in the upgrade notes Five inconsistencies predate this branch and survived the earlier passes because each one reads correctly on its own: - The backfill-source table says a custom network only gets the historical fork fields, while the list below it also names the migrated system-contract addresses that BackfillCustomMigratedFieldsFrom copies. - The strict-validation preamble asks for "non-zero usable values", but fork switches accept 0 (active from genesis) and only the system-contract addresses are checked for zero. - The checklist is written in Go field names while its reader verifies a genesis.json, so state once that the JSON keys are the names used in the example. - The minimal example predates the fork declaration requirement and names none of pragueBlock, osakaBlock or amsterdamBlock. - Note 6 refers to a "strong pinning warning" that is not marked as a warning anywhere; it means the pinning requirement in note 3. No other wording changes.
* fix(params): order the fork listings consistently ActiveForks documents that the list it returns is sorted in ascending ASCII order, but TIPEpochHalving was appended after TangerineWhistle, which sorts later. Move the append to its alphabetical slot, between TIP2019 and TIPIncreaseMasternodes. The benchmark-only mirror activeForksLegacyMapSort never carried Gas2500x: the production list gained the name in #2516 and the mirror was not updated with it. The gap stayed invisible because BenchmarkActiveForks did not schedule the fork either, so the benchmark would only have compared two different lists once some config scheduled Gas2500x. Enumerate the name in the mirror and schedule the fork in the benchmark, so both sides enumerate the same forks. Add TestActiveForksLegacyMirrorMatchesActiveForks, which pins the mirror to the production list. It compares the two implementations over a pair of configs that between them activate every fork - scheduling the two Disable fork blocks turns TIPXDCXMiner and TIPXDCXReceiver off and XDCxDisable on, so one config cannot cover every name - and it fails when either list gains or loses an entry or when the production order drifts from the ASCII order the mirror sorts into. String and Description render the fork fields in a different order than chainConfigForkOrderFields, the order CheckConfigForkOrder enforces. #2811 renumbered that order so DynamicGasLimit, TIPUpgradeReward and TIPUpgradePenalty activate before Prague and Osaka, but the two renderings were not moved with it: they kept Prague, Osaka and the Amsterdam line that #2756 added beside them ahead of that trio. Move the six fields into fork order and pin them, together with TIPEpochHalving that follows them, in TestChainConfigRenderingFollowsForkOrder, which reads the expected order off chainConfigForkOrderFields, so the fields it pins cannot drift out of fork order again. The package comment in params/forks promised an order the enum does not hold: it said the forks are ordered by XDC mainnet activation height with unscheduled forks last, but Constantinople, Petersburg and Istanbul, none of which mainnet scheduled, sit in the middle of the enum, ahead of TIPSigning at 3,000,000. Say what the enum is instead - a naming registry for the fork labels with no consensus meaning - and point at chainConfigForkOrderFields, together with chainConfigForkOrderSpecialCaseRules, for the order that is enforced. The ordering defects all predate this change: the ActiveForks list and its misplaced append came together in #2326; the missing mirror entry arrived in PR #2516, and the renderings were left behind by #2811. TIPEpochHalving is nil on all five live networks and on all six dev and test configs, so none of the reordering changes a chain's behaviour. What changes is the order of the fork list in the config banner, which prints every fork field regardless of scheduling, and, where the forks are set, the order of the String rendering and of the activeForks array in the chain config view. The docs entry for activeForks now states the ordering guarantee it relies on. * fix(params): render the dao fork line after tip2019 in the config banner chainConfigForkOrderFields puts TIP2019 (fork order 2) before DAOFork (fork order 3), and String already renders them that way, but the Description banner has listed DAO Fork and DAO Support ahead of TIP2019 since long before #2811. Move the two lines after TIP2019 so the banner follows the same order as String, and extend TestChainConfigRenderingFollowsForkOrder to the pair; the banner spells DAOFork as "DAO Fork", so the test maps that single label for it. With the last rendering divergence gone, the params/forks package comment can state that both renderings follow chainConfigForkOrderFields.
… on ethereum#20169 (#2772) Ref: geth ethereum#20169 (c476460) Refs #2771 CheckConfigForkOrder compared only pairs of forks that were both configured, so a config that scheduled Osaka while leaving Prague unset was accepted: every fork slot in this chain config is optional, so upstream's non-optional shanghaiTime lock has no counterpart here. Add a second scan over the standard fork slots in forkOrder order. Forks that decide a whole precompile bag or instruction set (Homestead, EIP150, EIP155, EIP158, Byzantium, Petersburg, Istanbul, EIP1559, Prague, Osaka, Amsterdam) must be declared before any later standard fork is scheduled; the other standard slots only trigger the check. Both sets live in config_backfill_fields.json and are validated by the generator. Petersburg is required unconditionally, which is stricter than the shape of its own consumer suggests: an unset PetersburgBlock on a chain that never declares ConstantinopleBlock runs the legacy rule either way, but upstream rejects that config, because its petersburgBlock and istanbulBlock entries are non-optional and geth ethereum#21473 states the intent as "fork ordering requirements which state that Petersburg fork be set if Constantinople fork is set". TIPXDCXCancellationFeeBlock stays the effective activation of both Istanbul and Petersburg, mirroring IsIstanbul and IsPetersburg, while TIPXDCXMinerDisableBlock deliberately does not count. That scan reports a gap only when a later slot carries a value, so it cannot see a schedule that stops at ConstantinopleBlock, where IsConstantinople is true while IsPetersburg stays false and the net-metered EIP-1283 SSTORE rule runs with no fork declaring it. chainConfigForkSequencePetersburgPair checks that pair on its own; declaring PetersburgBlock or activating it through TIPXDCXCancellationFeeBlock keeps the config valid. Post-Shanghai, upstream allows a skipped optional fork; this repository is stricter because its instruction sets and precompile bags are selected by flags rather than by the highest configured fork. See docs/adr/0002-require-standard-fork-sequence.md. The scan also supersedes the OsakaBlock -> AmsterdamBlock special case that #2756 added, together with the beforeRequired mechanism that only served it. Both forks are standard slots, so the scan already rejects "OsakaBlock is unset, but AmsterdamBlock is scheduled" with the wording that rule used, and the rule's premise (the linear comparison only covers forks that are both set) no longer describes the validation. Removing the rule, the beforeRequired field and its branch, and the two comments that referenced it changes no behaviour: the scan runs first and the error is identical. The ADR records why the rule could go while its wording stays. Two pre-existing gas2500x rejection cases only asserted the ErrWrongForkSwitchOrder sentinel, which the sequence scan now shares; they assert the full error string here. Each trigger-only slot carries a case of its own, so every standard slot is pinned as a rejection source rather than only as a slot the scan walks past. The field names the sequence scan compares and reports are constants checked against both the fork block and the fork order tables at init, so a rename in the generated table fails loudly instead of silently disabling the cancellation-fee fallback or changing which pair Petersburg is checked against.
#2777) The built-in XDC network configs left ConstantinopleBlock, PetersburgBlock and IstanbulBlock unset. At runtime ChainConfig.IsPetersburg/IsIstanbul fall back to TIPXDCXCancellationFeeBlock, so those two forks activated at the cancellation-fee height (mainnet 38383838, testnet 23779191, devnet 0), while IsConstantinople has no fallback and was never true on any of the three chains. Declare the three slots explicitly at those heights, following the upstream convention of setting a skipped fork at its successor's block (geth mainnet sets Constantinople and Petersburg both at 7280000), and mirror the values into the embedded genesis/{mainnet,testnet,devnet}.json so the two startup paths stay in lockstep. In the genesis files the trio keys sit next to DenylistBlock, the height they share, rather than after byzantiumBlock: those files group fields by activation height and their key order is an append history that has never matched the Go struct order. Execution is unchanged: GatherForks (hence forkid) is identical before and after, Istanbul rules were already active at these heights through the fallback, and the newly-true IsConstantinople only adds an ActiveForks entry. Because the slots now carry values, the fork-order validation moves them after TIPXDCXCancellationFeeBlock; without that, filling them is rejected with "IstanbulBlock 38383838 > TIPSigningBlock 3000000". The rejection also names the field to move: both the ordered scan and the special-case rules append the fix — declare the later field at or after the earlier one, plus, when that field is not mandatory for the config's shape, that dropping it works too — instead of leaving the operator to work out which of the two fields to move. The eight slots between Byzantium and the trio (TIPSigningBlock..TIPXDCXCancellationFeeBlock, including DenylistBlock) shift down by three so the ordering key stays dense at 1..34 and the trio takes 16..18. The standard slots keep their relative order (Constantinople 16 still sits before Berlin 20), so the sequence scan reads the same sequence it did before. Test fixtures that push the XDC forks into the future carry the trio together for the same reason, and new tests pin the declared heights, the unchanged fork id, and the configs the new position refuses. The built-in Localnet config stays unfilled, because it is the hydration source for custom chain configs. Nodes running a stored config from before this change need no action: the setup and load paths hydrate the stored config from the built-in one before the compatibility check, so the newly declared slots are backfilled rather than compared. Reorder the JSON field-table rows so the physical order of the trio follows the forkOrder key (the trio now physically follows TIPXDCXCancellationFeeBlock); the generator sorts the fork-order table by the key, so this changes no output: the trio stayed the last three rows of the common table, which is the order the semantic config digest reads. Row order is not free elsewhere - the common table keeps the JSON row order, and chainConfigForkBlockFields preserves it into that digest, so reordering its rows changes it. The built-in table is emitted as the standard fields followed by the XDC-specific ones, each group in JSON row order, so only a move inside a group changes it. The fork-order table's own rows carry no semantics: they were already not fully aligned with the key before this change and still are not (TIPXDCXMinerDisableBlock and TIPXDCXReceiverDisableBlock still follow the Cancun..Osaka rows), so the move is readability only. Pin the PR description's own rejection example as a test case: the scan collides with ConstantinopleBlock first, so that is the slot the error names. Move the trio in the Fork enum and in the label map next to the forks they share the 38383838 height with (Denylist through TIPTRC21Fee), so the three declarations sit together. String() and Description() move the trio the same way, so both renderings keep following the fork order, and TestChainConfigRenderingFollowsForkOrder now pins the trio together with the cancellation-fee anchor it sits after. The enum values carry no meaning: the package is reached only through String(), and no production code persists or compares a Fork; TestGasTierForksAreOrdered pins Gas50x before Gas2500x, which this move does not touch. The comment above the enum is left alone: it already describes the order as a naming registry rather than as an activation order. Pin the one user-visible effect, ActiveForks gaining "Constantinople" on the three built-in networks while Localnet stays nil, so that entry cannot drift silently. Update the statements this change makes stale: the IsIstanbul and IsPetersburg backward-compatibility notes in params/config_forks.go named the three networks as configs that leave the slot unset, and now name Localnet instead. The ADR that records the sequence check (docs/adr/0002-require-standard-fork-sequence.md) listed the three among the networks leaving the trio empty in its migration note, rested a rejected option on the same list, and recorded the fallback-versus-declared-slot rejection as out of scope; the reorder makes that rejection disappear in the direction the entry described and appear in the other one, so the entry is updated rather than dropped, and its config_forks.go line anchors follow those two comments to their new lines. Hydration of the three declared slots is a single decision: every slot the stored config takes a position on must already agree with the anchor the built-in networks declare, where a missing slot reads through the cancellation-fee fallback and an explicit null is authoritative: it is neither hydrated nor used to veto the others. No slot moves unless all of them agree, so a stored schedule never gains a fork-ID boundary it did not have, and the batch is re-validated as a whole and rolled back when the full startup check (CheckConfigForkOrder) rejects it. Fork-order repair hints follow the same rule: a slot is only offered as droppable after clearing it and re-validating the result, so a diagnostic cannot recommend a change the next validation rejects.
…ethereum#35588 ethereum#35914 (#2781) * fix(core,internal/ethapi): add fork validation in the state transition ethereum#35588 Messages built outside the transaction pool (eth_call, eth_createAccessList, eth_estimateGas, debug_traceCall and eth_simulateV1) bypass the signer, so a transaction type that is not enabled yet at that block was still accepted and executed there. The set-code case is reachable on mainnet, where EIP1559Block is set while PragueBlock is unset: every post-EIP-1559 header is a pre-Prague header. Testnet activates Prague at 87,633,000, so the same window exists before that block. eth_simulateV1 accepted an authorization list on one of them, applied the delegation, and handed back a type-4 transaction; the same chain then rejects that transaction as "pool not yet in Prague", so the block it returns cannot be replayed against the chain it was simulated for. Reject an access list before EIP-1559 and a set-code authorization list before Prague, both reporting ErrTxTypeNotSupported the way the transaction pool does. ported from geth 2dfea58 (ethereum#35588), which fixes the same bypass: the normal execution path is guarded by the signer, but eth_call and the state tests construct messages directly. The blob check added by that commit has no counterpart here, because this fork has no blob transaction type and Message carries no blob fields. Upstream receives the fork rules as a preCheck argument; this port adopts that signature, with execute resolving the rules before the call, so the check and the rest of the function read the same rules instead of re-reading the chain config. The access list check is gated on EIP-1559 rather than on the Berlin activation the upstream commit uses. This fork schedules Berlin and EIP-1559 at different blocks (mainnet 76,321,000 and 98,800,200), and its transaction pool rejects every non-legacy type, access list included, until EIP-1559 is active. Gating on Berlin would leave a 22,479,200-block window where eth_call still executes a transaction the pool refuses; following the pool keeps the two layers in step. eth_createAccessList needed one deviation. It attaches the access list its tracer produced to the message, and that list is non-nil even when it is empty, so before EIP-1559 every call, with or without a caller-supplied list, would have been rejected. The computed list is now attached only once EIP-1559 is active; a list supplied by the caller still reaches the message and is still rejected before the fork. Upstream attaches it unconditionally and therefore rejects the method before Berlin; the divergence keeps the method usable there, which is the behaviour before this commit. An empty JSON access list ("accessList": []) decodes to a non-nil empty slice, which the state transition reads as a type-1 marker. ToMessage now drops it, so the request behaves as if the list were omitted. Verified on Linux amd64 with Go 1.25.14: - make all, gofmt and goimports are clean; go test ./core ./internal/ethapi passes and golangci-lint --new-from-rev reports no new issues. - TestPreCheckRejectsTransactionTypesBeforeTheirFork covers both types on both sides of their fork: removing a guard fails its rejection case, and making the check unconditional fails the accepted cases. - TestApplyMessageRejectsAccessListBeforeEIP1559 covers the production call path, where execute resolves the rules. - TestApplyMessageAcceptsAccessListAtEIP1559 covers the accepting side on the same production path. - TestSimulateV1RejectsAuthorizationListBeforePrague covers the end-to-end case on top of the merged simulation guards: an eth_simulateV1 call carrying a valid authorization list and no gasPrice against a post-EIP-1559, pre-Prague header fails with "transaction type not supported: setcode tx" instead of returning a type-4 transaction. - TestCreateAccessListBeforeEIP1559 and TestCreateAccessListRejectsCallerListBeforeEIP1559 cover the method before EIP-1559: the computed list is returned but not attached, so gasUsed leaves out the EIP-2930 intrinsic cost, while a caller-supplied list is still rejected. - TestToMessageDropsEmptyAccessList covers the empty JSON list, which must not reach the message as a type-1 marker. Ref: ethereum#35588 * fix(internal/ethapi): reject 1559 fee fields in calls before the fork ethereum#35914 eth_call, eth_estimateGas, eth_createAccessList, eth_simulateV1 and debug_traceCall ignore maxFeePerGas and maxPriorityFeePerGas on a block whose header has no base fee. ToMessage drops them in that case, so a call with a 2 gwei fee cap runs with GASPRICE 0, and eth_simulateV1 hands back a dynamic-fee transaction that the pool then rejects as "pool not yet in EIP1559". Reject the fields in CallDefaults when the base fee is nil, using the message that setFeeDefaults already returns for the same case. A zero value counts and gasPrice stays valid. eth_createAccessList targets a block rather than the chain head, and the two can sit on different sides of the fee fork. It now derives the fee defaults from the target block's header (setDefaultsWithHeader) instead of the head; otherwise the head's 1559 defaults would be filled in and the check above would reject a historical call that never supplied them. This ports geth 8a8efd3 (ethereum#35914). The upstream message names London; this fork schedules London and EIP-1559 at different blocks (mainnet 76,321,000 and 98,800,200), so the wording follows setFeeDefaults and names EIP-1559. The check still keys on the base fee rather than the fork rules, so a baseFeePerGas block override before the fork is not rejected by this check. ToMessage still uses the header's base fee, so that combination is not a working call path (it yields nil fee fields); that mismatch predates this change. Two simulate tests paired the 1559 fee fields with a block that carries no base fee: TestSimulateV1 passed them to a contract sender, and TestSimulateV1BaseFeeNonValidationMode to its first block. Those calls now leave the fields out; both tests exercise other behaviour and the fee fields were silently ignored there before. Verified on Linux amd64 with Go 1.25.14: make all, gofmt and goimports are clean; go test ./internal/ethapi ./eth/tracers passes. - TestCreateAccessListBeforeEIP1559WithoutFeeFields covers a head past EIP-1559 with a target block before it and no fee fields in the request. Ref: ethereum#35914
) GenerateChain dropped the error returned by consensus.Engine.Finalize, so a failing finalization (for instance a reward hook error) was silently discarded: every engine in this tree returns a nil block together with the error, so the generator committed the failed block's state and handed back a chain holding a nil block. With n == 1 that nil block reaches the caller; with n > 1 the next iteration dereferences it in parent.Root(). In this fork Finalize returns (*types.Block, error), while its geth counterpart returns nothing (geth removed the error return in ethereum#19301, 4c90efd; ethereum#34726 later removed FinalizeAndAssemble), so there is no upstream fix to port. Surface the failure in the same style as the state and trie write error handling right below, and keep the GenerateChain signature unchanged. Ref: #2605
These twelve generated files were last written in January 2025, before the v1 abigen template was rebuilt by the ethereum#31379 backport and before the template fixes that landed here with #2645 (geth ethereum#25574), #2628 (geth ethereum#35701) and #2631 (geth ethereum#32164). Regenerating now brings all of them in at once, which is why it was held back until those three were merged. The input is the ABI and bytecode already committed in these files, so the payload is unchanged byte for byte and only the shape of the generated code moves forward: XxxMetaData is added and XxxABI/XxxBin become deprecated aliases, Deploy* and bind* use MetaData.GetAbi() instead of re-parsing the ABI JSON on every call, library linking no longer substitutes into the shared package-level XxxBin, the Parse<Event> helpers appear, and the Watch<Event> loops skip a log whose signature does not match instead of ending the whole subscription. One API reduction: types with no bytecode, namely the abstract interfaces ITRC21, AbstractXDCXListing, AbstractTokenTRC21, LAbstractRegistration, LAbstractTokenTRC21 and LAbstractXDCXListing, no longer get XxxBin and DeployXxx, because the template guards both on a non-empty bytecode input. Nothing in the tree references them. Regenerating means running the in-tree abigen against a combined-json document rebuilt from the checked-in ABI and bytecode. Two details matter when doing so: the combined-json "bin" field must hold bare hex, since the template prepends the 0x itself, and XDCValidatorBin is the one literal in these files that has no 0x prefix. Verified with make all and make quick-test (127 packages, no failures).
…le in the session, close #2611 (#2783) * fix(core/state): invalidate session cache in DeleteAddress (#2611) DeleteAddress loaded the account into the live cache via getStateObject before deleting it from the trie, and deleteStateObject only removed it from the trie. The cached object therefore stayed readable for the rest of the session: GetBalance, Exist and GetCode kept reporting the pre-deletion account, while IntermediateRoot/Commit already reflected the deletion, so in-session reads disagreed with the final state. Drop the object from the live cache, record a deletion mutation and mark the account destructed, the same way the self-destruct path does, so the removal is visible to the rest of the session. The deletion mutation replaces any pending update for the address, which would otherwise be applied against the missing object. Marking the account destructed additionally lets handleDestruction track the original account value as a deletion at commit time. The removal is journalled as well. DeleteAddress is a public API, and dropping the object from the live set made a later journal revert read a missing object: balanceChange, nonceChange, codeChange and storageChange all dereference getStateObject, so reverting past the call panicked. The new journal entry carries a copy of the object and of the block-scoped bookkeeping the removal overwrites; reverting to a snapshot taken before the call restores all of it. The account trie entry is deleted by the next IntermediateRoot, so the revert does not have to write it back. The only production caller, ApplyTIPSigningHardFork, runs before the first transaction of the activation block. Both networks set TIPSigningBlock to 3,000,000 and have long passed it; that block carries no transactions on either network (mainnet and Apothem: txs=0, gasUsed=0), it is not a reward checkpoint, transactions addressed to the account are routed to ApplySignTransaction which only bumps the sender nonce, and the non-EVM log helper only appends logs. No historical block therefore observes or recreates the account, and the state root is unchanged. A network that sets TIPSigningBlock in the future must confirm its activation block contains no transaction that reads or recreates the account. The API has behaved this way since it was introduced in 7833a10 (2018). geth has no DeleteAddress and no corresponding fix to port; the destruct-marking mirrors the in-repo self-destruct, Finalise and handleDestruction machinery. Fixes: #2611 * docs(eth,tracers,internal/ethapi): refresh TIPSigning DeleteAddress comments These comments still claimed DeleteAddress removed the account from the trie only and left the cached state object readable in the same session, which is no longer true after the session-cache fix. Update them: the removal is now visible in-session, and the missing traceBlock/traceChain cases in #2591 are a deliberate trade-off rather than an impossibility. * docs(core/state): correct the stale StateDB.Copy snapshot caveat The doc said a snapshot of the copied state cannot be applied to the copy, but journal.copy copies validRevisions and nextRevisionId, so a snapshot taken before the copy does revert on the copy. TestDeleteAddressCopyRevert relies on that behaviour. The caveat predates this branch (present at 1d7ebe2:core/state/statedb.go:811) and go-ethereum master 943517b carries the same wording with the same implementation, so it is corrected in its own commit instead of being folded into the DeleteAddress change.
…thereum#35888 (#2786) When a generated v1 binding deploys a contract that links a library, the library is deployed first and the main contract then asks the txpool for the next nonce. The pool updates its pending nonce asynchronously, so the main contract can be handed the library's nonce and fail with "replacement transaction underpriced". WaitAccepted does not help, because it returns as soon as the transaction sits in the pool, before the nonce is updated. Set the nonce to the library transaction's nonce plus one instead of asking the pool again, and copy the opts first so the caller's are left untouched. The upstream patch needs two adaptations on this fork: - The fork template guards the acceptance wait with !auth.NoSend, so the nonce bump sits inside that guard: a transaction that is never submitted cannot move the pool, and forcing a nonce would change the semantics of NoSend deployments. - The if/end guards around the opts copy carry whitespace-trim markers, so a binding without library dependencies generates byte-for-byte the same code as before. Without them every no-library Deploy function would gain a blank line and the checked-in contracts/ bindings would go stale the moment this lands; regenerating all twelve was re-run and confirmed to stay a byte-for-byte no-op. Library linking keeps substituting into the function-local bytecode copy from #2631 rather than the package-level XxxBin, so the two changes compose. TestBindLibraryDeploymentWait now also asserts the opts copy and the redirect to it, the nonce bump, both positions before the library deployment and the bump, the bump's placement inside the NoSend guard and after the acceptance wait completes, and the absence of both in a binding without library dependencies. With several library dependencies it also checks that the opts are copied once and that the nonce advances after every library transaction. Ref: ethereum#35888 Verified with make all and make quick-test (no failures).
… per call (#2787) Both helpers parsed the TRC21 ABI out of its JSON string on every call: core's GetTokenAbi ran for every apply-class transaction while the XDCX receiver fork was active (the pool-admission and miner callers pass a nil block number, which the validators substitute with the current header number before the gate, so on an activated fork those paths reach the ABI exactly like the import path), and XDCx's GetTokenAbi ran on every token decimal cache miss. The regenerated binding merged as #2784 now exposes the same ABI through TRC21MetaData.GetAbi(), which parses once per process and returns the cached *abi.ABI, so consume it directly at the three call sites and drop the two thin wrappers, which also removes the last use of the strings package in both files. No behaviour change: MetaData.GetAbi() returns the same ABI defined by the exported string, and the cached value is only read. The caching contract itself is pinned by accounts/abi/bind tests, and the XDCx call site is pinned by TestGetTokenDecimalUnpacksWithCachedTRC21ABI, which fails when the call site packs with an ABI that lacks the TRC21 decimals method. It also fixes a stale doc comment on TestMetaDataGetAbiCachesParse in accounts/abi/bind: the comment said the cached accessor had no caller, which held when #2722 wrote it (no GetAbi() call site existed under contracts/ at that commit) but stopped holding once #2784 regenerated the v1 bindings. Ref: #2785
…lose #2788 (#2789) * fix(accounts/abi/abigen): resolve v1 identifiers in a stable order The v1 generator walked evmABI.Methods and evmABI.Events directly, and both are maps. Two methods can normalize to the same Go identifier - the NumericMethodName fixture has _1test and __1test, which both become M1test - and which of them keeps the name and which one is given a suffix was decided by abi.ResolveNameConflict in whatever order the map yielded. The same ABI therefore generated two different bindings: in one run M1test was 0xffa02795 (_1test) and M1test0 was 0xd02767c7 (__1test), in the next the two swapped. Nine regenerations produced two distinct files. Walk the sorted keys instead, for the events as well, since they claim identifiers the same way. The order of the generated methods never depended on this - text/template sorts map keys when ranging over one - but two other things did: the competing identifier claims, and the names of anonymous tuple types, which are numbered Struct%d in the order the walk first meets them. The lexicographically first original name now keeps the unsuffixed identifier, which is the choice abigen v2 already makes: testdata/v2/numericmethodname.go.txt has E1test for 0xffa02795 and E1test0 for 0xd02767c7. Upstream carries the same unordered loop (accounts/abi/abigen/bind.go:129), so this is a fork-side fix rather than a port. Reported as #2788. Verified with make all, make quick-test (no failures), and by regenerating the v1 golden corpus of the next commit twenty times in a row without a difference. * test(accounts/abi/abigen): pin the v1 bindings against golden output TestBindings generates the same contracts, but it only compiles and runs them, so a v1 template change that still produces working code goes unnoticed: the template can drop a caching layer, emit a different helper or stop generating a deprecation alias, and every test stays green. The checked-in bindings under contracts/ shipped for eight months in exactly that state (#2652). Add the output of every case in bindTests as testdata/v1/<name>.go.txt and compare the generator against it, one file per case. Regenerate with WRITE_TEST_FILES=1, the switch the v2 corpus already uses. A single comparison only catches an unstable walk when the map happens to yield the other order, so TestBindingV1IdentifierOrder generates the colliding cases 64 times as well. It also pins which original name keeps the unsuffixed method and event identifier, and that anonymous tuple types keep their Struct0/Struct1 names, which the golden files only cover with a single struct. go-ethereum keeps golden output for v2 bindings only, so there is nothing to port; the mechanism follows testdata/v2 and TestBindingV2ConvertedV1Tests. Checked that the corpus notices what the existing tests cannot: with the template's bind{Type} parse point reverted to abi.JSON(strings.NewReader(...)) - the divergence reported as #2620 - the new test fails while TestBindings still passes. Verified with make all, make quick-test (no failures), twenty consecutive runs of the new test without a difference, and three regenerations producing byte-identical output.
) Nothing in the repository imports XDPoSChain/bmt: a grep over the whole tree finds no importer outside the package itself, and the Makefile, the CI test selection and the docs never reference it. Upstream go-ethereum deleted the top-level bmt/ in e187711 (2018-06-20, "swarm: network rewrite merge") and the swarm/bmt copy left the tree with geth ethereum#19661 (2019), so no maintained upstream revision is left to compare against. Removing the package also drops the three data races reported in #2792 (run() vs finalise() walking the same tree nodes, plus the write-only ha.hash field) from a -race sweep, so the package needs no exclusion there. Closes #2792 Closes #2796 Ref: ethereum#17041
…close #2794 (#2799) The fairness tests read genIter.index with atomic.LoadUint32 in Next, but Close assigned it with a plain write. When FairMix.Close ran while a reader was still iterating, TestFairMix and TestFairMixNextFromAll reported a data race under -race. Write the field atomically, matching the upstream fix in ethereum#23434. The dial scheduler also stored the connection flags of every added peer with a plain read, d.peers[id] = c.flags, while Server.run sets the trusted flag on the same word through a sync/atomic CAS, so go test -race ./p2p/ reported a race between p2p/dial.go and p2p/server.go. The map is used purely as a set -- assignment, delete, len and membership check, never the stored value -- so store struct{}{} instead and drop the racy access, again matching the upstream fix. Ref: ethereum#23434
#2791 (#2800) Port the internal/testlog half of upstream ethereum#29301 ("eth/protocols/snap, internal/testlog: fix dataraces"): bufHandler gains a sync.Mutex which Handle, WithAttrs and logger.flush now hold while touching the record buffer, and the struct literals are switched to named fields. The file was identical to the upstream revision before that commit except for the import paths, so the port needs no local adaptation. The snap sync_test.go half of that commit is not applicable: this tree has no eth/protocols/snap package. Ref: ethereum#29301 Local verification on xdc/dev-upgrade tip 3095c15: - go test -race -short -count=1 -run 'TestServerListen|TestServerDial| TestServerPeerLimits|TestServerSetupConn' ./p2p/ reported DATA RACE before the change; the testlog races are gone after it. - make quick-test and make all pass; gofmt/goimports are clean. The branch has since been rebased onto 3777382; make all still passes there, and now that #2794 has also landed go test -race -short -count=1 ./p2p/ is green end to end (2/2 runs).
The traceTx watcher only stopped the tracer when the per-transaction deadline expired, so a caller that had already gone away kept the trace running until its own timeout. Stop on any reason the deadline context is done, and mark the trace as finished before the deferred cancel() runs, so a watcher woken by that cancel() never stops a tracer whose execution already finished. The new tests trace a contract that only ends when its gas runs out: with the default, unlimited struct logger every executed opcode is kept, which is tens of gigabytes for 500M gas and enough to send the host into the OOM killer. Bound the collected logs in those tests; they never inspect them. Ref: #2640
… (#2802) The handler tests increment their counters with atomic.AddUint32 from the Bfter.loop goroutines, but the assertions read them with plain loads, so the test goroutine races with the broadcast handlers it just triggered: go test -race reports four data races (bft_handler_test.go:92, :236, :288 and :363) and fails TestSequentialVotes, TestTimeoutHandler, TestSyncInfoHandler and TestTooFarTimeout. Which of them fail depends on when the race reports land, so the failing set varies between runs. Read every counter with atomic.LoadUint32 in the assertions, both in the conditions and in the t.Fatalf arguments; the increment sites are left untouched. go-ethereum never had a bft package (git ls-tree HEAD eth/ lists no bft entry, and a case-insensitive grep over the master tree finds nothing), so there is nothing upstream to port. The code entered this fork with a1b77f3 (XDPoSChain PR #13, 2021-11-06), whose eth/bfter/bft_test.go carries the same plain-read pattern. Closes #2793
#2732 (#2807) The two test backends in this file replayed the TIPSigning and Multicall3 block level pre-execution hooks but not the third one production applies before the first transaction, so the pre-state they handed to a tracer was missing the EIP-2935 parent hash history write block processing performs. params.TestChainConfig activates Prague at block zero, so every fixture in this file produced a pre-state short of that write. Mirror the production order in both backends: build the EVM first, then apply the three hooks, then replay the transactions. Constructing the EVM ahead of the hooks also puts the empty block early return on the production shape, which eth/state_accessor.go already has, so an empty block now carries the history write the way it does in production. No production code changes. Upstream go-ethereum has no corresponding fix to port: its tracer fixtures rebuild no block level pre-execution at all.
…, close #2738 (#2813) * fix(cmd/utils): report a malformed block at its position ethereum#27213, close #2738 The CLI importer drops the exporting node's genesis block while decoding, but counted it only after the skip, so n held the number of blocks kept rather than the position in the input stream. The message that names a parse error read n, so a malformed block came out one position early: a file cut short behind the genesis block reported "at block 0" instead of "at block 1". Count the block before the skip, which is what the RPC importer has done since The other message that read n, "invalid block %d", no longer reads it: it names the first block InsertChain says did not make it (upstream go-ethereum ethereum#27213), so moving the count cannot shift it, and a batch that fails in the middle blames that block instead of the end of the batch or a position past the file. Upstream go-ethereum has no corresponding fix to port for the counting order: its importer counts after the same skip, and no commit there touches that order. Its invalid block message already reads the insert index, which is the shape this commit copies (upstream: geth ethereum#27213 for that part). Local verification: make all, gofmt and goimports, go run build/ci.go lint ./cmd/utils/..., and go test ./cmd/utils/. TestImportChainReportsTheFailingBlockPosition fails with the counting moved back, reporting "at block 0"; TestImportChainReportsTheBlockTheInsertFailedOn fails against the n-based invalid block message, reporting block 4 instead of 2. Ref: ethereum#27213 * fix(core): keep the nil database error out of the merkle root message ValidateState renders statedb.Error() through %w unconditionally, so the common merkle root mismatch - execution completed, no database error recorded - came out as "dberr: %!w(<nil>)". Add the dberr segment only when a database failure was recorded; the message without it is what the mismatch always meant. The non-nil path is unchanged. Upstream go-ethereum carries the same unconditional %w (core/block_validator.go:201, introduced by ethereum#26870) and has no fix to port, so this is a deliberate divergence; the cmd/utils importer test that pins the failing block now also rejects a nil formatting verb in the message. Local verification: make all, gofmt and goimports, go run build/ci.go lint ./core/... ./cmd/utils/..., go test ./core/ ./cmd/utils/ ./eth/, and TestImportChainReportsTheBlockTheInsertFailedOn fails with the condition removed, reporting the %!w(<nil>) text.
…lose #2735 (#2812) eth_simulateV1 applies overrides before the block level state changes, so an override of a field those changes write is undone or trips the consensus panic. Refuse it on Multicall3, the EIP-2935 history contract and the legacy block signers account at their Prague and TIPSigning blocks, and for a move of an active precompile onto the history contract; a move from an account that is not one cannot shadow the contract, and override.Apply rejects it before the guard would need to. Fields no change writes stay overridable (#2711 covers Multicall3), and a refusal names the lowest written slot and the lowest moving account, so the response does not depend on map iteration. Applying the overrides later would undo that guard and move the panic to the next block, postponing it instead of removing it. Upstream has nothing to port: geth neither installs code nor panics. The guard sees the simulated block alone; #2855 tracks the rest.
ethereum#34812, close #2734 (#2809) * refactor(core,eth,internal/ethapi,miner): consolidate block level hooks ethereum#34812, close #2734 Block processing and every block level replay apply the same three hooks in the same order before the first transaction: ApplyTIPSigningHardFork and ApplyMulticall3HardFork mutate the statedb, then ProcessParentBlockHash writes the parent block hash history through the EVM. The sequence was copy-pasted at nine production points (core/state_processor.go twice, core/chain_makers.go, miner/worker.go, eth/state_accessor.go, eth/tracers/api.go three times, internal/ethapi/simulate.go) and mirrored at four test points (eth/tracers/api_test.go twice, eth/tracers/api_tipsigning_activation_test.go, eth/tracers/api_intermediate_roots_test.go). Missing one when adding or reordering a block level state change desynchronises the pre-state a tracer, eth_simulateV1 or the miner works against, and the failure mode is a wrong intermediate root or a wrong trace rather than a compile error. Add core.ApplyBlockLevelHooks, which applies the sequence in the canonical order, and call it from all thirteen sites. The helper takes only the parent hash and the EVM: the caller owns the EVM because the sites build it for their own use (block processing reuses it for the transactions, the miner for the work task) and only some of them need the extra state a miner or a tracer attaches to it, and the activation config and the block number are read from that same EVM. The three hooks therefore read their gate from that one EVM instead of from two parameter lists plus the EVM context; parentHash is the one input vm.BlockContext does not carry. Every write goes through evm.StateDB, so a caller cannot hand the helper a state the transactions do not run against. The DAO fork is deliberately outside the set: misc.ApplyDAOHardFork stays open coded at the call sites that process or generate blocks, the way upstream core.PreExecution leaves it out (ethereum#34812). Moving it in would apply it on the replay paths for the first time. Behavior changes beyond moving code: - parentState is now copied before all three hooks. The baseline copied it after ApplyTIPSigningHardFork and ApplyMulticall3HardFork and before ProcessParentBlockHash, while the miner takes work.parentState in makeCurrent before running any of them, so the import path now matches the miner. The helper applies the three hooks in one call, so the baseline's split cannot be reproduced without applying the hooks twice: the copy has to move as a whole, which is why it lands with the consolidation instead of in a commit of its own. HookReward only reaches masternode contract slots (GetCandidatesOwnerBySigner, GetVoters and GetVoterCap in contracts.GetRewardBalancesRate), none of which the hooks write, and the state root comes from the statedb, so no reward or root changes. At DAOForkBlock the two snapshots still differ by the DAO balances, because Process copies after misc.ApplyDAOHardFork while the miner copies before it; HookReward reads no balances, so the reward output still agrees. A future HookReward read outside those slots has to keep both snapshots in step. - The Multicall3 install now writes through evm.StateDB. Block import wraps the state in a tracing wrapper only when vm.Config.Tracer is set, which is the --vmtrace path, so that path reports the account creation (OnCodeChange /OnNonceChange) where the baseline reported nothing. The state root is unchanged. debug_trace* hands the hooks the plain state and is unaffected. eth_simulateV1 hands them the wrapped state too, but its tracer carries call and log hooks only, no state-change hooks, so it is unaffected as well. - core/state_processor.go moves the EVM construction above the hooks at both sites. In ProcessBlockNoValidator the first stop check now precedes the parentState copy, the EVM construction and the hooks, so a stop raised before the call runs none of the three hooks (the DAO fork above still ran). A stop raised while the hooks run only reaches the second check, which now sits below them, so that calculation runs all three where the baseline ran the two statedb mutations and skipped the EIP-2935 write. The state is discarded on either path. In Process, InitSignerInTransactions, balanceUpdated and totalFeeUsed also move below the hooks; InitSignerInTransactions only makes and caches signers, and the other two are initialisers, so none of the three touches the state. - core/chain_makers.go and eth/tracers/api.go (traceChain) build the EVM unconditionally now, the way the geth equivalent of those sites does for core.PreExecution; outside Prague the call is a no-op. - The two eth/tracers/api_test.go backends keep the EVM construction above their empty block early return, the shape PR #2807 gave them, and now run the sequence through the helper instead of the open coded calls. TestApplyBlockLevelHooksReportsInstallToTheTracer and TestProcessReportsMulticall3InstallToTheTracer pin the install reaching the tracer through the EVM state, the second one through the production Process call site. TestApplyMulticall3HardForkReportsInstallToTheTracer keeps covering the hook in isolation. TestProcessRunsBlockLevelHooksBeforeTheFirstTransaction pins that the hooks run ahead of the first transaction, so the transaction already sees the install and the history write. TestProcessSnapshotsParentStateBeforeBlockLevelHooks pins the reward snapshot on the import path, and TestProcessBlockNoValidatorSnapshotsParentStateBeforeBlockLevelHooks pins the same snapshot on the validator-free path, plus that a stop raised before the call returns ahead of the snapshot and the hooks. Closes #2734 Ref: ethereum#34812 (core.PreExecution consolidates the upstream pre-execution system calls; it is not a straight port: ApplyTIPSigningHardFork and ApplyMulticall3HardFork are statedb mutations specific to this chain, and the gate here comes from the EVM while upstream passes it as arguments) Verification: gofmt and goimports clean, make all, golangci-lint --new-from-rev=f285bf505b (0 issues), go vet ./core/..., go test -count=1 ./core/... . make tidy and make generate are not run: no dependency and no generated file changes. * fix(core): hand GenerateChain's Finalize the parent snapshot GenerateChain copied the state inside the Finalize call, after the block's transactions had run through BlockGen.AddTx and after the block level hooks, and handed that copy to Finalize as parentState. Process and ProcessBlockNoValidator take the snapshot before the hooks, and the miner takes work.parentState in makeCurrent, so a generated block whose transactions write the masternode slots reached HookReward with a snapshot block processing never uses. Take the snapshot before the hooks and before the transactions, the way block processing does. The state root is unaffected: it comes from the statedb, not from the snapshot. The output only differs when a generated block is a reward block and its transactions write the slots contracts.GetRewardBalancesRate reads; GenerateChain and the consumers built on it (ethclient/simulated) now agree with block processing there. TestGenerateChainSnapshotsParentStateBeforeTransactions pins the transaction half: a generated block that transfers to an account Finalize watches must not see the transfer in parentState. TestGenerateChainSnapshotsParentStateBeforeBlockLevelHooks pins the other half: the snapshot predates the block level hooks, so it does not carry the Multicall3 install the processed state has. Upstream has no counterpart: geth's consensus.Finalize does not take a parent state, so there is nothing to port. Verification: gofmt and goimports clean, make all, go vet ./core/..., golangci-lint --new-from-rev=f285bf505b (0 issues), go test -count=1 ./core ./ethclient/simulated. * docs(eth/tracers): point the legacy traceBlockParallel precondition at ApplyBlockLevelHooks The comment predates the helper: it came in with #2266 and tells callers to apply "block-level pre-execution system calls", the vocabulary the GLOSSARY now avoids, leaving each caller to rediscover the sequence. Name core.ApplyBlockLevelHooks instead, the single entry point every block level replay uses. No code change: comment only.
* refactor(consensus/tests): remove unused v2 helper copies GetSnapshotSigner and GetCandidateFromCurrentSmartContract in consensus/tests/engine_v2_tests/helper.go have no callers: the only references in the tree are the v1 helpers of the same name, which are live and untouched. Their dedicated types signersList and masterNodes were used by nothing else, so they go with them, together with acc4Key and acc5Key, two package-level keys that were never referenced anywhere in the package. Verification: make all, go vet ./... (compiles all _test.go), make quick-test. * refactor(consensus/XDPoS): drop unused nm slice in UpdateMasternodes The slice was filled inside UpdateMasternodes and never read afterwards. Because it is only appended to, the compiler does not flag it. No behaviour change. Verification: make all, go vet ./..., make quick-test. * refactor(consensus,core/types,ethstats): remove dead forensics proof chain The forensics reporting chain has been unreachable since #1517 (1a2c8ee) commented the real implementations out and replaced them with empty shells, and every test in consensus/tests/engine_v2_tests/forensics_test.go that asserted on its events carried t.Skip("Skipping this test for now as we disable forensics"). What is left of the reporting path therefore never runs in production: - SendForensicProof, FindAncestorBlockHash, getQcSignerAddresses, findQCsInSameRound, generateForensicsId, reverse, SendVoteEquivocationProof, generateVoteEquivocationId and GetVoteSignerAddresses had no caller outside the commented-out blocks, so the imports they were the only users of go as well. - The only senders of the forensics feed were those two functions, and both were themselves called only from the /* ... */ blocks, so the feed never fired: Forensics.SubscribeForensicsEvent, the subscription scope and the feed go as well, together with the XDPoS facade that forwarded to them. - ethstats subscribed to that feed and reported proofs it never received. Drop the subscription, the forwarding goroutine and reportForensics. - The five types in core/types/forensics.go (ForensicsInfo, ForensicsContent, VoteEquivocationContent, ForensicProof, ForensicsEvent) had no user left once the producers and the consumer were gone. - forensics_test.go loses its five event-asserting tests and keeps the three that only exercise the entry points that stay: VoteHandler, SetCommittedQCs and ForensicsMonitoring. engine_v2/forensics_test.go is deleted whole: its single test covered findQCsInSameRound, and SignHashByPK / RandStringBytes / getSignerAndSignFn had no reference anywhere else in the package. - The commented-out implementations are not kept as code: they cannot compile again without the helpers removed above, so they are trimmed to design-note doc comments on the three shells - intent, flow-diagram links, and what a re-enable needs - with the full versions left to git history. Kept on purpose: the Forensics type with HighestCommittedQCs, SetCommittedQCs and ForensicsMonitoring, and the three shells that are still called from the live path (ProcessForensics from ForensicsMonitoring, ProcessVoteEquivocation and DetectEquivocationInVotePool from vote.go). Breaking change: the exported SendForensicProof, FindAncestorBlockHash, SendVoteEquivocationProof and GetVoteSignerAddresses are gone, as are Forensics.SubscribeForensicsEvent, XDPoS.SubscribeForensicsEvent and the five core/types forensics types. No caller inside this repository is affected. Verification: gofmt, goimports, make all, go build ./..., go vet ./..., go test on ethstats, core/types, consensus/XDPoS and engine_v2_tests (ok). * test(consensus/tests): re-enable the retained forensics tests (pre-existing) The three tests that survived the dead forensics removal were still gated by "Skipping this test for now as we disable forensics", a line added in #663 long before #1517 disabled the reporting path. They only exercise the entry points that stay - VoteHandler, SetCommittedQCs and ForensicsMonitoring - and all three pass once the skip is removed (5.23s / 0.16s / 0.18s), so the message no longer describes anything real. No production code change: the gate predates this branch, which is why this lands as its own commit rather than folded into the removal. Verification: make all, gofmt, go test -count=1 ./consensus/tests/engine_v2_tests/ (ok).
) * refactor(core,XDCx,XDCxlending,miner,consensus): remove dead code and mgo dependency The matching-result chain lost its consumers when the mongodb support removal (#1679) dropped the code that read the results back, so the data was produced and never consumed: - BlockChain.AddMatchingResult / AddLendingResult / AddFinalizedTrades had no callers, and the five caches they wrote, their initialisation, and the crypto import used only by them were never read. - The local tradingResult / lendingResult maps built in ValidateTradingOrder and ValidateLendingOrder had no readers either. - The trades themselves were never read: on the XDCx side they were a []map[string]string returned by CommitOrder/ApplyOrder and only packed into tradingstate.MatchingResult by ProcessOrderPending, which the miner then discarded, while on the lending side ApplyOrder returned []*lendingstate.LendingTrade directly. Either way the container reached callers that dropped it, so the trades slice/field no longer crosses the API; the cache-key helpers go with the types. - The tradeRecord map in XDCx/order_processor.go existed only to fill that discarded container, and tradingstate.Trade (with its BSON helpers) was never instantiated anywhere, so tradingstate/trade.go is deleted whole. The remaining BSON faces are dead in the same way: the GetBSON/SetBSON methods and their helper structs on OrderItem, EpochPriceItem, LendingItem and LendingTrade were only reachable through mgo, and state storage uses RLP, so there is no trigger path at all (no direct .GetBSON()/.SetBSON() call, no bson.Getter/Setter assertion, and no mgo API call anywhere in the tree). SignatureRecord and the two zero-caller ComputeHash methods go with them; LendingTrade.ComputeHash stays - it is called from the order processor. globalsign/mgo loses its last import, so go mod tidy removes it from go.mod/go.sum. Every state-mutating settle/match path is kept: SubAmountOrderItem, SetLastPrice, SetMediumPrice, InsertOrderItem, CancelOrder, DoSettleBalance and the LendingTrade object written through InsertTradingItem. The rejects path is kept too, because it is still consumed by the reject logging inside ProcessOrderPending (XDCx/XDCx.go, XDCxlending/XDCxlending.go); the two rejects returned in core/block_validator.go were always discarded there. A review follow-up folded into this commit removes the rest of what only existed to serve the dropped containers: the XDCx.XDCX.sdkNode field (unread since #1679 removed SyncDataToSDKNode, its only writer), the bson:"..." struct tags on LendingItem / LendingTrade (their json:"..." tags stay - that is the live serialization contract), and three return values whose results every caller discarded: getTradeQuantity loses its *SettleBalance result (the DoSettleBalance call inside it is untouched), and Lending.ProcessTopUp / Lending.ProcessRepay lose their *LendingTrade result (the object is still built and written to state inside the functions). EpochPriceItem itself goes with those decoders: after its BSON methods and ComputeHash are gone, the type has no reference left in the tree (it was already unconstructed on dev-upgrade), so its file is deleted whole, and ToBigInt - called only from the removed SetBSON decoders - goes with it. Breaking change: this narrows the exported surface. The signatures below lose their first (trades) return value, so implementations of the two interfaces, and callers of these methods, outside this tree stop compiling: - consensus/XDPoS/utils: TradingService.ApplyOrder, LendingService.ApplyOrder - XDCx: XDCX.CommitOrder, XDCX.ApplyOrder, XDCX.ProcessOrderPending - XDCxlending: Lending.CommitOrder, Lending.ApplyOrder, Lending.ProcessOrderPending Two more exported methods lose their *lendingstate.LendingTrade result; for these the LendingTrade object is still built and written to state inside the function, only the returned handle is gone: - XDCxlending: Lending.ProcessTopUp, Lending.ProcessRepay (Lending.ProcessTopUpLendingTrade / Lending.ProcessRepayLendingTrade keep their signatures - the liquidation path still consumes their results.) Exported symbols deleted: tradingstate.MatchingResult, tradingstate.GetMatchingResultCacheKey, tradingstate.EpochPriceItem (file deleted whole), tradingstate.ToBigInt, lendingstate.MatchingResult, lendingstate.GetLendingCacheKey, lendingstate.ToBigInt, tradingstate.Trade together with the trade-record constants of tradingstate/trade.go (file deleted whole), the GetBSON/SetBSON pair and BSON helper structs of OrderItem / EpochPriceItem / LendingItem / LendingTrade, SignatureRecord, EpochPriceItem.ComputeHash, LendingItem.ComputeHash, BlockChain.AddMatchingResult / AddLendingResult / AddFinalizedTrades. Verification: make all, go build ./..., go vet ./... (compiles all _test.go), make quick-test (-short -failfast), make tidy. * refactor(XDCx): drop unused UpdateOrderCache UpdateOrderCache, introduced by b5abbfe, has no callers in the tree: on the dev-upgrade baseline it is already only the definition, and the orderCache field it writes is never read anywhere. tradingstate. OrderCacheLimit and tradingstate.OrderHistoryItem exist only to size and type that cache, and GetOrderHistoryKey is called only from inside UpdateOrderCache, so all four go with it. The lru import stays - tokenDecimalCache still uses it. Verification: make all, go build ./..., go vet ./... (compiles all _test.go), make quick-test. * refactor(XDCx,XDCxlending): drop unused math and relayer helpers These helpers had no callers before this branch either: whole-program reachability (golang.org/x/tools/cmd/deadcode) and a reference count over all .go files, tests included, both leave only the declaration. - XDCx/tradingstate: Mul/Div/Add/Neg/Exp/Max wrappers, EmptyOrderList and EmptyExchangeOnject (nothing reads them; state code uses big.Int methods and constructs its own empty values) - XDCxlending/lendingstate: the same wrappers, WeiToEther and the token-list helpers superseded by tradingstate's copies (lendingcontract.go queries tradingstate directly) Intentional survivors: EtherToWei and GetExRelayerFee are still used by lendingstate tests, lendingstate.DecodeFinalizedResult has a live call site in core/block_validator.go, and Sub/CloneBigInt stay in use.
… bson remnants (#2814) The MongoDB backend of the XDCx database was removed in PR #1679. This drops the code that only existed to serve it: - XDCxDAO: drop the mongo-only XDCXDAO methods (GetObject, InitBulk, CommitBulk, InitLendingBulk, CommitLendingBulk), their empty leveldb implementations and the uncalled list helpers (DeleteItemByTxHash, GetListItemByTxHash, GetListItemByHashes); the package comment no longer claims a mongodb backend - ethdb, core, eth: drop the ethdb.XDCxDatabase interface, the BlockChain.XDCxDb handle and the four NewBlockChainEx* wrappers; eth/backend.go now calls NewBlockChainResolved directly, and the startup test that named the wrapper goes with them - XDCxlending: remove the uncalled Lending.GetLevelDB wrapper; the XDCx accessor it delegated to stays, since New still uses it, and the Lending.XDCx field keeps its other readers in order_processor.go Follow-on cleanups on the same theme: - XDCx/tradingstate: give orderList its own doc comment (it carried a copy of the tradingExchangeObject one) and state what tradingExchangeObject holds: the main trie is keyed by the order book hash, with the orderId trie as the sub-trie rooted at OrderRoot. The same wording corrects the two sibling stateObject blocks in state_orderList.go and state_orderbook.go; all three were carried in from b5abbfe. - XDCxlending/order_processor.go: the liquidationData comment said the data goes to mongodb; it is written to the trade's extraData - docs/xdc/debug: drop the one mgo goroutine block (a github.com/globalsign/mgo newcoarseTimeProvider select loop) from the debug_stacks_response.json fixture, left over from a capture taken while the mgo dependency was still in the tree; nothing in the tree mentions mgo or globalsign any more The rest of this branch's original scope -- the bson marshallers and their dedicated structs, the bson struct tags, the ToBigInt helpers, the dead Trade type and epochpriceitem.go, and the mgo entry in go.mod/go.sum -- landed on dev-upgrade first (#2805, #2806). After rebasing onto them those hunks are gone, so this commit only carries what was still missing. Comment-only and dead-code changes; no behaviour changes. No upstream equivalent: geth has no XDCx module.
Hashes come from the official golangci-lint-2.15.0-checksums.txt; the linux-amd64 archive was downloaded and its sha256 spot-checked against that file. The .sbom.json and -source.tar.gz assets are excluded, matching the previous layout and upstream geth. Verified with make all, plus a lint run that resolved, checksum-verified and executed the v2.15.0 binary (params package reports the same two pre-existing copyloopvar issues as before). Ref: #2839
Update the # version:golang pin in build/checksums.txt, which selects the distribution fetched by -dlgo for release builds, from 1.25.12 to 1.25.14. go.mod is untouched. The bump picks up two Go minor releases on the 1.25 line: - go1.25.13 (released 2026-08-13) includes security fixes to the go command, crypto/tls, encoding/asn1, encoding/xml, html/template, net/http and net/url, plus bug fixes to the compiler, the runtime, crypto/tls and os. Milestone: https://github.com/golang/go/issues?q=milestone%3AGo1.25.13+label%3ACherryPickApproved - go1.25.14 (released 2026-08-19) includes fixes to the net/http package. Milestone: https://github.com/golang/go/issues?q=milestone%3AGo1.25.14+label%3ACherryPickApproved Release notes: https://go.dev/doc/devel/release#go1.25.13 and https://go.dev/doc/devel/release#go1.25.14 (upstream geth cites the golang-announce post for the same purpose, geth ethereum#35935). The pin stays on the 1.25 line so the release toolchain matches the rest of the repository: CI runs 1.25.x, cicd/Dockerfile builds with golang:1.25 and go.mod declares go 1.25. Moving ahead to 1.27 is not possible yet: golangci-lint 2.12.2 and golang.org/x/tools v0.37.0 cannot read Go 1.27 export data, so make lint and make generate break as soon as a developer installs the pinned toolchain (measured on 1.27.0, 1.27.1, 1.27.2 and 1.27.3; upstream pins its lint job to 1.27.1 for the same reason, geth ethereum#35927). Hashes are taken from https://go.dev/dl/?mode=json&include=all. The archive list is unchanged: 41 entries, freebsd-riscv64 included. Verified by letting build.DownloadGo checksum-verify go1.25.14.linux-amd64.tar.gz (sha256 a21ae563...), extract it and build every command in ./cmd/... with the extracted toolchain (go version go1.25.14); make all and make tidy pass.
…#2818) Remove the toolchain line instead of raising it. This tree already ships its own toolchain download system: -dlgo fetches the Go release pinned in build/checksums.txt, which is the single source of truth for release builds. Leaving a toolchain directive around lets Go's built-in switching fetch a compiler behind that pin's back, or fail outright when GOTOOLCHAIN=local runs an older Go. Adopt geth ethereum#31057 verbatim; the same reasoning applies here, so the directive is dropped rather than bumped. The go directive stays at 1.25, so module resolution is unchanged. make tidy reports no untidy module files and make all passes. Ref: ethereum#31057
…reum#27387 ethereum#34109 (#2819) TestUDPv5_findnodeHandling can fail when the table revalidation timer fires while the test is waiting for NODES responses. Revalidation pings the last node of a random non-empty bucket, and nodes inserted by fillTable have no UDP port and are not registered in the test harness, so the background PING is taken for an expected packet and fails with "attempt to send to non-existing node 3.0.2.3:0". Expose the interval as Config.PingInterval, defaulting to the existing 10s so production behaviour is unchanged, and pass it to the table. The UDPv5 test harness sets it to 1000 hours, disabling revalidation for tests. A test overlay that accelerates the interval reproduces the failure without the harness setting and passes with it. Non-positive intervals fall back to the default as well: a negative value would otherwise reach rand.Int63n from Table.nextRevalidateTime and panic the Table.loop goroutine. ethereum#27387 only handles zero and upstream still does, so this part has no upstream counterpart. Very small positive values remain allowed and will revalidate continuously; the field comment documents that. newTable now takes the Config and applies withDefaults on its first line, mirroring the single-point defaulting of ethereum#27387. This fork keeps revalidating from Table.loop instead of table_reval.go, so neither that rewrite nor the Config field stored on Table is carried over. The Config knob backports go-ethereum ethereum#27387, which this fork predates; the harness setting is the fix from ethereum#34109, whose one-line change depends on that knob. Upstream has since moved revalidation into table_reval.go. Ref: ethereum#27387 Ref: ethereum#34109
…on (#2820) * fix(cmd/puppeth): reject unusable XDPoS schedules in genesis generation The interactive wizard collects the epoch and the gap as two independent answers, so a gap of 0, one equal to the epoch or one longer than it could reach the genesis. None of the three gives the v2 trigger a usable gap block: with a zero gap it asks for n%Epoch == Epoch, which no height satisfies; with a gap longer than the epoch the subtraction wraps and no height matches either; with a gap equal to the epoch the trigger lands on the epoch switch itself, where engine_v1's (number+Gap)%Epoch == 0 accepts the offset, BlockChain.isGapBlockNumber (gap <= epoch) sides with engine_v1, while BlockChain.isNextEpochGapBlock refuses it, so the checkpoint predicate and the snapshot predicate disagree about which block carries the next epoch's snapshot. Judge the pair once, before anything is derived from it and before the system contracts are deployed: the epoch is written into the BlockSigner contract deployed further down, so failing early leaves no genesis behind and skips the deployment work. The interactive answers are judged where they are read, before the int to uint64 conversion can turn a negative one into a huge positive value: an epoch below 2 and a negative gap end the run, an explicit gap has to stay inside the epoch, and a zero gap takes the default. The BlockSigner parameter is a uint256, so the epoch has no int64 ceiling and is deployed with SetUint64. The interactive gap default halves the epoch instead of staying at a fixed 450, so it sits inside the bound whatever epoch was answered; at the template epoch of 900 it is still 450. The default is derived by a helper the test can pin directly, and the interactive run is covered end to end as well: a child process answers the wizard, leaves the gap empty and the stored genesis must carry half the answered epoch. The two schedule questions are asked only when their answers are read, so the input-file path no longer prints prompts reporting the template epoch and half of it for a pair it does not produce. The input file had the mirror problem for the keys it cannot apply: gap, switchBlock and switchEpoch have no GenesisInput field, so yaml dropped them silently and the genesis kept the cloned Localnet template's values while the file said otherwise. Refuse such a file and name the keys, and say in the guard that the coverage stops there: period, rewardCheckpoint, maxMasternodesV2, skipV1Validation and v2 have no field either and are still dropped without a word. A child process feeds a file that names gap and the run must exit before writing, so the refusal is covered end to end. The epoch field is the one schedule field the file can carry, so keep applying it on top of the template and let the same check judge the resulting pair; the input-file path is the only non-interactive way to choose an epoch. A file with no fields at all is refused as well: an empty file, a comment-only one and an explicitly empty mapping ({}) would all decode to nothing and silently produce the template defaults, and yaml hands back a non-nil empty map for {}, so the guard judges the length alongside the nil. The bound is open-coded in checkXDPoSSchedule because the config-level rule is not on this branch yet: it lands in ChainConfig.CheckConfigForkOrder, and the body should then defer to it instead of restating the range. * fix(cmd/puppeth): fold genesis input keys instead of dropping them The input file is decoded by matching keys against the lowercased Go field names exactly, while the schedule guard already folds case and underscores before comparing. A file that spelled a field like its Go name (Epoch:, Masternodes_Owner:) therefore decoded to the template default without a word, the same silent divergence the guard refuses for gap, switchBlock and switchEpoch. Fold the recognised keys onto the spelling yaml matches before decoding, and refuse a file that carries two spellings of one field instead of letting the map order pick a winner. Keys with no GenesisInput field (period, rewardCheckpoint, maxMasternodesV2, skipV1Validation, v2 and anything misspelled) now warn instead of disappearing silently; they stay accepted so existing input files keep working. The decoder has matched exact lowercase keys ever since the input file was introduced, so this fixes an older hole rather than one this branch opened.
…2822) A config that declares XDPoS could carry any Epoch/Gap pair into the engines. CheckConfigForkOrder only checks the SwitchBlock alignment and skips it when Epoch is 0, so both engine constructors rewrote a missing epoch to 900. The core gap predicates then disagree on Gap == Epoch, and for Gap > Epoch the v1 engine still walks Gap blocks back from a checkpoint to heights those predicates never report. Validate the bound in core at every point that resolves a config before the engine is built (Genesis.commit, SetupGenesisBlock and the LoadChainConfig resolvers) and again where a resolved config opens a BlockChain. params and consensus stay untouched, so a violating config now fails at startup instead of being warned about. This replaces the params-level default-and-continue answer of #2560.
ethereum#31539 ethereum#35324 (#2825) * fix(internal/testlog): compare levels in the right direction ethereum#29572 bufHandler.Enabled compared the record level the wrong way round: it returned lvl <= h.level, so a logger configured with log.LvlTrace (LevelTrace = -8) accepted only trace records and silently dropped debug, info, warn and error. Every other handler in log/ uses the opposite convention (TerminalHandler returns level >= h.lvl), and go-ethereum fixed the same line in ethereum#29572. Only that line is ported: the rest of ethereum#29572 rewrites peer discovery revalidation (table_reval.go), which this tree still implements in Table.loop, so the commit cannot be taken wholesale. Ref: ethereum#29572 * fix(internal/testlog): log through the handler created by With ethereum#31539 logger.With copied the parent's *bufHandler into the sub-logger, but log.Logger.With builds a new handler through bufHandler.WithAttrs and attaches that to the returned logger. Records written through the sub-logger therefore went to a handler that flush never read: its output was never printed and its buffer grew without bound. Take the handler from the logger returned by With instead; the type assertion is safe because a testlog logger is always built on a *bufHandler. This is also where the T interface comes from: with it, a unit test can pass a fake that captures Logf, which *testing.T cannot. LoggerWithHandler is removed along with it, as upstream did; nothing in this tree called it, and Logger(t T, ...) still accepts every existing call site, all of which pass a *testing.T. Ref: ethereum#31539 * fix(internal/testlog): clone the attribute list before appending ethereum#35324 WithAttrs and terminalFormat appended to h.attrs directly. When the slice has spare capacity the append writes into an array a sibling handler still points at, so the two overwrite each other's attributes. Clone the slice first, as upstream does. The hazard is not reachable in this tree today: every call site applies With at most once to a freshly built logger, whose attrs slice has no spare capacity yet. Keeping a capacity-dependent aliasing bug in a test helper is still not worth it when the fix is one call. Ref: ethereum#35324 * test(internal/testlog): cover the level filter, sub-loggers and attributes One regression per upstream fix ported on this branch: - at log.LvlTrace an info record has to reach t.Logf (Enabled direction, ethereum#29572); - a logger obtained from With has to print its own records, attribute included (they used to land in a handler that flush never read, ethereum#31539); - appending attributes must not write into the slice a sibling handler still points at (ethereum#35324). None of this is observable from the outside: a *testing.T cannot capture its own Logf, which is why the T interface came with ethereum#31539. * fix(internal/testlog): serialize With with log flushes (pre-existing race) logger.With derived the sub-logger from a copy of the parent's buffer (bufHandler.WithAttrs copies h.buf) without holding the logger mutex, while every log method holds l.mu across its Handle and flush. A With running in that window makes the child inherit a record the parent is about to print as well, and the child's first flush prints it a second time. Hold l.mu across With, the same critical section the log methods use: the buffer copy then always observes a fully flushed state. Lock order stays l.mu -> h.mu (WithAttrs takes only h.mu), so no new deadlock is possible. The race predates this branch: the baseline builds sub-loggers the same way, and go-ethereum has no corresponding fix -- internal/testlog is unchanged upstream since ethereum#35324, even though go-ethereum derives sub-loggers from the same logger in p2p exactly as this tree does. The two added lines are therefore a deliberate divergence from upstream, and the window they close is not hypothetical: p2p hands the test logger to the server as Config.Logger, and p2p/server.go derives a sub-logger from it in every connection goroutine while the listen loop and the peer loop log on it concurrently. The regression test parks a logger inside its flush -- a T that blocks in Logf -- and requires that deriving another sub-logger from the same logger cannot return while that log call is in flight. It does not depend on two goroutines happening to overlap: without the lock the With returns at once and the test fails, with the lock the With cannot return at all.
* fix(core): keep the removed-logs delivery test off the scheduler TestReorgDeliversRemovedLogsSynchronously recorded the arrival in the subscriber goroutine after the channel handoff, and read that flag once the reorg returned. An unbuffered handoff only orders the send against the receive: whatever the subscriber does next runs in parallel with the rest of the reorg, so a subscriber the scheduler put off for a few hundred microseconds made a synchronous send look asynchronous. The new race job (-p 4 on a 4 vCPU runner) failed on it in #2797 with the reorg having waited the full subscriber delay. Announce readiness before parking on the receive instead: that write is sequenced before the handoff, which in turn happens before the send the reorg waits for returns. Carry the arrival over a buffered channel and compare its log count against the chain that was reverted. Both ends of that channel are bounded by a timeout, so a reorg that never sends fails the test without stranding the subscriber goroutine. Upstream geth has no equivalent test and sends synchronously as well, so there is nothing to port. Tests: 160 runs of the test as four processes pinned to four cores under GOMAXPROCS=4, 7 of which failed before this change and none after; turning the send into a goroutine still fails the test within 0.02s, and dropping the send altogether fails it at once on the armed check, with the subscriber goroutine then leaving the receive within its own timeout. make all, go test ./core and go run build/ci.go lint ./core/... (no new findings) pass. * test(core): widen the removed-logs delivery budget The subscriber delay is not only a stall: it is also how long an asynchronous send may take before the check can no longer tell it apart from a synchronous one. reorg hands the removals over near its start (both rmLogsFeed.Send calls precede the rollback and the head writes), so a spawned send has to outlast the delay before it hides. At 250ms that window was ~12x the ~20ms of remaining reorg work measured under -race, so a slow runner could pass a regression silently. Raise it to a second, which leaves ~50x, and record the premise next to the constant: the budget tracks where the send sits in reorg, not the subscriber. Spawning the send still fails the test within 0.01s, so the guard is unchanged. The test and its 250ms budget predate this branch - the previous commit only changed how the arrival is recorded - so this lands on its own commit instead of being folded into it. Tests: make all; go test ./core (24.3s); the test under -race (1.02s); 40 runs as four processes pinned to four cores under GOMAXPROCS=4, none failing; lint on ./core/... (26 issues the package already had, none in the diff).
) SimNode.Start registers the services before the devp2p node is started, and node.RegisterProtocols appends to the p2p server's protocol slice in that window. The simulations HTTP API marshals the node info from another goroutine: the event stream does it as soon as a control event is handed over, and the node handlers do it on request. Neither side takes a lock, so a node that is created and then started races with the HTTP layer reading the protocol slice. The new race job in #2797 reported it as TestHTTPSnapshot failing on p2p.Server.NodeInfo reading srv.Protocols against node.RegisterProtocols appending to it. Hold the startup lock for the whole of Start and take it shared in NodeInfo. The lock order stays startMu -> node.lock, the only other lock in the path, and nothing reached from Start calls NodeInfo, so readers wait for the startup instead of observing a half-filled server. The change is confined to the in-memory adapter and leaves the event and node response payloads alone. Upstream geth deleted p2p/simulations in ethereum#30250 without fixing this, and both NodeInfo and RegisterProtocols are still unsynchronised there, so there is nothing to port. Tests: 100 runs of TestHTTPSnapshot as four concurrent processes under -race (short, count=25 each) report no race; dropping the read lock again makes 3 of the 4 processes fail, so the lock is what keeps it quiet. make all, go test ./p2p/simulations/... pass, golangci-lint 2.12.2 on ./p2p/simulations/... reports no new findings, and make tidy reports no untidy module files.
…Duration (#2831) * fix(common/countdown): fix data race between SetParams and GetTimeoutDuration ExpTimeoutDuration stored duration/base/maxExponent without synchronisation while the countdown goroutine read them, so a concurrent SetParams could be observed torn and a rejected parameter set could be partially visible. Guard the three fields with a sync.RWMutex: GetTimeoutDuration copies them under the read lock, SetParams validates the candidate set before taking the write lock and publishes all three at once. Rollback is no longer needed because an invalid set is never written. The signatures are unchanged. NewExpTimeoutDuration now returns a nil helper along with the error instead of a helper holding the rejected values; its only caller already drops the helper on error. Add a concurrent SetParams/GetTimeoutDuration test that reports the data race on the old code under -race, plus a test pinning that a rejected set leaves the timeout unchanged. The latter covers both rejection branches and keeps all three parameters observable, so a partial write cannot slip through. Also correct the pre-existing ")= >=" typo in the validation message now owned by checkExpParams, and assert the previously discarded SetParams errors in TestExpDuration. Fixes #2828 * fix(params): correct the max_exponent upper-bound validation message validateV2ExpTimeoutConfig printed "max_exponent (%d)= >= max_exponent_upperbound (%d)". Drop the stray "=" so the message matches the one emitted by checkExpParams in common/countdown for the same bound. Message text only; the validation behaviour is unchanged. * refactor(common/countdown): read initilised under RLock CountdownTimer.isInitilised took the write lock to read a single bool, while the read path of ExpTimeoutDuration takes the read lock. Use RLock for the read so both helpers in the package follow the same rule. No behaviour change: initilised is only written by setInitilised, and the two helpers are never called while the other's lock is held. Pre-existing (blame 521b703), not introduced by the SetParams and GetTimeoutDuration race fix.
feat(eth/hooks,params): add signing-proportional reward hard fork
|
Important Review skippedToo many files! This PR contains 279 files, which is 179 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.
|
gzliudan
approved these changes
Oct 26, 2026
* fix(node): release datadir lock when initialization fails * fix(node): clean up temporary keystore on initialization failure Signed-off-by: Vladislav Lapin <51929896+loglapa@users.noreply.github.com> * fix(node): close account manager on initialization failure Signed-off-by: Vladislav Lapin <51929896+loglapa@users.noreply.github.com> * test(node): track manager goroutines across failed initialization Signed-off-by: Vladislav Lapin <51929896+loglapa@users.noreply.github.com> --------- Signed-off-by: Vladislav Lapin <51929896+loglapa@users.noreply.github.com>
make lint fails on 18 whitespace violations: a blank line right
before a closing brace ("unnecessary trailing newline") or right
after an opening brace ("unnecessary leading newline") across the
consensus, core, internal and p2p packages.
golangci-lint reports at most three issues of each kind per run
(max-same-issues defaults to 3), so the debt surfaced piecemeal.
This removes all of it in one pass; no code behavior changes.
…2889) The module targets Go 1.25, where each iteration of a for-range loop binds a fresh instance of its variables, so the explicit per-iteration copies ("tt := tt", "mode := mode", ...) kept to guard closures and subtests against loop-variable reuse are no longer needed and confuse the copyloopvar linter. Remove all 11 copies it reports across core, eth/downloader, p2p/discover/v5wire and params. No behaviour change: the closures now capture the per-iteration loop variable itself.
…2892) golangci-lint's revive receiver-naming rule flags five methods whose receiver name deviates from the name the rest of the type uses: - TxPool.SetSigner and TxPool.IsSigner used "pool" while the other 18 TxPool methods use "p" (upstream geth uses "p" throughout as well) - XDCxLastPrice.Name and XDCxEpochPrice.Name used "c" while the remaining methods of both types use "t" - Miner.SetGasTip used "miner" while the other 14 Miner methods use "m" Each rename follows the majority convention already established in its file, so the diff is a pure rename with no behavior change.
The unconvert linter flags a conversion when the value already has the target type. Four such conversions survived in the tree: - uint64(params.TxGas): TxGas is already declared uint64 - types.Transactions(block.Transactions()): Block.Transactions() already returns types.Transactions - []byte(obj.code): the state object code field is already []byte - int(n.LogVerbosity): NodeConfig.LogVerbosity is already int Remove them. No behaviour change. The sibling conversion types.Transactions(entry.Transactions) stays: Body.Transactions is a plain []*Transaction slice, so that one is load-bearing.
…eum#35176 (#2894) The govet inline check added in the go1.26 toolchain flags two patterns across the tree, and make lint reports 21 of them - the last linter debt nobody has claimed: - reflect.Ptr, the deprecated alias whose constant value is now trivially inlinable, in accounts/abi, log, rlp and rpc (19 hits); - ioutil.ReadFile and ioutil.TempDir in two p2p tests, which can no longer be inlined into files compiled with an older language version (2 hits). Swap reflect.Ptr for reflect.Pointer, mirroring the upstream sweep in ethereum#35176, and move the two test call sites onto the os package equivalents (os.ReadFile, os.MkdirTemp) that upstream already uses. No behaviour change; a cold golangci-lint run with --enable-only govet reports zero issues. Ref: ethereum#35176
wanwiset25
approved these changes
Oct 27, 2026
anunay-xin
approved these changes
Oct 27, 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.
Proposed changes
Merge
dev-upgradeintomain: 50 commits since the last merge in #2810.Highlights:
eth_getRewardByHashstill works on proportional reward files.0 < gap < epoch(fix(cmd/puppeth): reject unusable XDPoS schedules in genesis generation #2820, fix(core,cmd/utils): reject XDPoS schedules outside 0 < gap < epoch #2822)Types of changes
What types of changes does your code introduce to XDC network?
Put an
✅in the boxes that applyImpacted Components
Which parts of the codebase does this PR touch?
Put an
✅in the boxes that applyChecklist
Put an
✅in the boxes once you have confirmed below actions (or provide reasons on not doing so) that🤖 Generated with Claude Code