Skip to content

fix(params,genesis): declare constantinople/petersburg/istanbul blocks - #2777

Merged
gzliudan merged 1 commit into
XinFinOrg:dev-upgradefrom
gzliudan:fix-config-fork-effective-blocks
Oct 18, 2026
Merged

gzliudan merged 1 commit into
XinFinOrg:dev-upgradefrom
gzliudan:fix-config-fork-effective-blocks

Conversation

@gzliudan

@gzliudan gzliudan commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Proposed changes

XDCMainnetChainConfig, TestnetChainConfig and DevnetChainConfig leave ConstantinopleBlock, PetersburgBlock and IstanbulBlock unset. At runtime IsPetersburg and IsIstanbul fall back to TIPXDCXCancellationFeeBlock, so those two forks activate at the cancellation-fee height (38383838 on mainnet, 23779191 on testnet, 0 on devnet), while IsConstantinople has no fallback and is never true on any of the three chains. Declaring those values was rejected by CheckConfigForkOrder with IstanbulBlock 38383838 > TIPSigningBlock 3000000: the value was right, the slot's position in the validation order was what refused it, because the slots sat before the XDC TIP forks whose activation height they share. Issue #2773 records the analysis and the escape routes.

This PR declares the three slots at their effective heights and moves them after TIPXDCXCancellationFeeBlock in the fork-order table, next to the anchor whose height they share (the slots between TIPSigningBlock and TIPXDCXCancellationFeeBlock activate at or before that height on the built-in networks). The JSON rows are reordered to follow the forkOrder key (the trio now physically follows TIPXDCXCancellationFeeBlock); the generator sorts by the key, so the row order carries no semantics: it is not a validation sequence and was already not fully aligned with the key before this change (TIPXDCXMinerDisableBlock/TIPXDCXReceiverDisableBlock still follow the Cancun..Osaka rows); only forkOrder is semantic in that table. The embedded genesis/{mainnet,testnet,devnet}.json gain the same three keys so the XDC init <network> path and the --<network> path stay in lockstep. In those files the keys sit next to denylistBlock, the height they share, rather than after byzantiumBlock: the genesis files group fields by activation height and their key order is an append history that has never matched the Go struct order. Setting all three follows the upstream convention for a skipped fork — geth mainnet declares Constantinople and Petersburg both at 7280000 — and matches the runtime fallback, so the declaration changes no behavior: GatherForks() is byte-identical before and after (hence the fork id), Istanbul rules were already active at these heights through the fallback, and the newly-true IsConstantinople only adds an ActiveForks entry, which is surfaced by the XDPoS_getConfig RPC and nowhere on the wire. Nodes holding a stored config from before this change need no action: the setup and load paths hydrate the stored config from the built-in one (BackfillMissingFieldsFrom) before the compatibility check, so the newly declared slots are backfilled rather than compared. Hydration of the three slots is a single decision: every slot the stored config takes a position on must already agree with the built-in height, with a missing slot read through the cancellation-fee fallback and an explicit null treated as authoritative (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 if the full startup validation (CheckConfigForkOrder) rejects it. Chains that deliberately avoid Constantinople while keeping the cancellation fee at the built-in height must therefore declare constantinopleBlock as an explicit null.

The costs are the ones #2773 predicts. Test fixtures that push the XDC forks into the future must now carry the trio together at the anchor height (setXinFinForksToFuture in core, the engine v1/v2 test helpers, contracts/utils_test.go, the three TIPSigning-activation fixtures, and the js tracer precompile fixture), and a custom config that declares the trio at a pre-cancellation-fee height (for example constantinopleBlock = petersburgBlock = 0) together with a later tipXDCXCancellationFeeBlock is rejected at startup with TIPXDCXCancellationFeeBlock 1000000000 > ConstantinopleBlock 0 — the scan collides with ConstantinopleBlock first, so that is the slot the error names. The built-in Localnet config stays unfilled on purpose: it is the hydration source for custom chain configs, and filling it would plant a zero in every private chain that omits these slots.

Fixes #2773.

Summary

 params/config_networks.go
   XDCMainnetChainConfig
-    ConstantinopleBlock: nil
-    PetersburgBlock:     nil
-    IstanbulBlock:       nil
+    ConstantinopleBlock: big.NewInt(38383838)   // = TIPXDCXCancellationFeeBlock
+    PetersburgBlock:     big.NewInt(38383838)
+    IstanbulBlock:       big.NewInt(38383838)
   TestnetChainConfig     // same three at 23779191
   DevnetChainConfig      // same three at 0

 params/config_backfill_fields.json (forkOrder)
   ByzantiumBlock 7
   TIPSigningBlock 8
   ...
   TIPXDCXCancellationFeeBlock 15
+  ConstantinopleBlock 16
+  PetersburgBlock 17
+  IstanbulBlock 18
   TIPTRC21FeeBlock 19
   ...                       // unchanged from here on; TIPSigningBlock..TIPXDCXCancellationFeeBlock each shift -3 so the key stays dense at 1..34

 genesis/{mainnet,testnet,devnet}.json
   "tipIncreaseMasternodesBlock": 5000000,
+  "constantinopleBlock": 38383838,
+  "petersburgBlock": 38383838,
+  "istanbulBlock": 38383838,
   "denylistBlock": 38383838,

Everything below lands in the same commit. CheckConfigForkOrder now names the slot to move when the order check refuses a config, in both the ordered scan and the special-case rules, and the params/forks enum is reordered so Constantinople, Petersburg and Istanbul sit with the other 38383838 forks, with String() and Description() following the same move: the enum documents itself as a naming registry for the labels returned by String(), and its numeric values carry no meaning (the package is only reached through String() outside tests). params also gains the mirror of the rejection test — a custom config that declares the trio at or after the anchor is accepted.

Evidence

  • Fork id unchanged, measured rather than argued: GatherForks() with the change stashed and reapplied is byte-identical for all three networks (mainnet [1 2 3 4 3000000 3464000 5000000 38383838 76321000 80370000 80370900 98800200 98802000], testnet [... 23779191 56828700 61290000 66825000 71550000 71551800 83600000 87633000], devnet [2700 25000 50000 1385100]), so the handshake filter accepts the same peers as before.
  • Rules() flips the three flags true exactly at 38383838 / 23779191 / 0; IsConstantinople is the only newly-true flag, and the instruction set and precompile set are unchanged because IsIstanbul is checked first at the same height.
  • Genesis hashes unchanged (TestEmbeddedGenesisHashMatchesParams); embedded configs match the canonical params configs (TestEmbeddedGenesisConfigMatchesParams).
  • Local runs on the commit: gofmt/goimports clean, make all, make generate (no stale files), make tidy (no module change), make quick-test (no failures), and the -short run over every package except ./node (its test binds a hardcoded 127.0.0.1:7878, occupied by a local web server on this dev box) — no failures.

Merge Danger

Door: two-way for the repository, one-way for an operator whose private chain config declares any of the three slots earlier than the last declared fork that precedes them in the fork-order table. On the built-in networks that anchor is tipXDCXCancellationFeeBlock. When that slot is unset in a bare ChainConfig the comparison anchor degrades to tipXDCXLendingBlock, or whichever fork before the trio is the last one declared; on a real startup path it does not degrade, because the omitted slot is backfilled from LocalnetChainConfig at 0 (BackfillMissingFieldsFrom, or BackfillCustomMigratedFieldsFrom for a config carrying an XDPoS section), so the refusal names TIPXDCXCancellationFeeBlock 0 instead. Two shapes hit it, both grown out of the "Constantinople-era chain" intuition that #2773 records: constantinopleBlock/petersburgBlock declared right after byzantiumBlock while the XDC forks are scheduled much later (rejected with TIPXDCXCancellationFeeBlock 23779191 > ConstantinopleBlock 4), and the same config with tipXDCXCancellationFeeBlock dropped (rejected at startup with TIPXDCXLendingBlock 1000000000 > TIPXDCXCancellationFeeBlock 0 — removing the slot does not rescue the shape: the omitted height is backfilled to 0, so the refusal moves to that slot; the TIPXDCXLendingBlock 23779191 > ConstantinopleBlock 4 form is what a bare ChainConfig produces). No edit both satisfies the order and preserves history: raising the trio to the anchor height, or lowering the XDC forks, changes the rules for the blocks in between, so re-validating existing blocks would diverge. The honest options for those chains are staying on the previous release or accepting that as a rule change — the same framing as #2772. A config that leaves the trio unset is not affected: nil slots are skipped by the order scan.

Checking an existing chain: read the config block of the genesis.json the node starts with, or the chain config already persisted in the datadir, and list the declared fork heights (for a genesis file, count a key it omits as backfilled: the startup path fills the XDC fork heights from LocalnetChainConfig, whose tipXDCXCancellationFeeBlock is 0). Walk tipSigningBlock, tipRandomizeBlock, tipIncreaseMasternodesBlock, denylistBlock, tipNoHalvingMNRewardBlock, tipXDCXBlock, tipXDCXLendingBlock, tipXDCXCancellationFeeBlock in that order and note the last one that is actually declared; if any of constantinopleBlock, petersburgBlock, istanbulBlock is declared and lower than that height, the new binary refuses to start. The refusal names the slot to move — for the example above it is TIPXDCXCancellationFeeBlock 1000000000 > ConstantinopleBlock 0: declare ConstantinopleBlock at or after TIPXDCXCancellationFeeBlock (no drop hint: Petersburg sits at the same height, so clearing only Constantinople leaves the same violation) — so the fix is reading the error. Chains that omit all three slots, and the three built-in networks, are unaffected.

Overlap with #2772: #2772 (merged as 714476f966, this branch's base) marks IstanbulBlock as a required slot in the same fork-order table this PR reorders, so the two constraints stack — a config declaring istanbulBlock must both satisfy the required-sequence scan and clear the anchor above. This branch is already based on that merge, so no rebase is pending.

Upgrade path: operators of the three built-in networks, and of custom chains that omit these slots, upgrade in place — swap the binary and restart; no config change, no rewind, no re-init, because the genesis hash is unchanged and the setup and load paths backfill the slots from the built-in config (BackfillMissingFieldsFrom) before the compatibility check, keeping a slot unset when injecting it would fail startup validation. Only operators of the shapes above have a decision to make.

All five built-in networks resolve: the three XDC networks now declare the slots explicitly, MainnetChainConfig and the unfilled Localnet are untouched.

Blast Radius: params startup validation plus the three built-in configs and three embedded genesis files; the fallout in tests is the fixtures that push the XDC forks into the future (eight sites across eight files, the trio added or raised to the anchor height) and the three golden chain-config semantic hashes. New tests pin the declared heights of the three built-in networks (Localnet staying nil), the unchanged GatherForks() output for all three, and the three config shapes the new position refuses (including the example above), plus the ActiveForks entry the newly-true IsConstantinople adds.

Types of changes

What types of changes does your code introduce to XDC network?
Put an ✅ in the boxes that apply

  • build: Changes that affect the build system or external dependencies
  • ci: Changes to CI configuration files and scripts
  • chore: Changes that don't change source code or tests
  • docs: Documentation only changes
  • feat: A new feature
  • fix: A bug fix
  • perf: A code change that improves performance
  • refactor: A code change that neither fixes a bug nor adds a feature
  • revert: Revert something
  • style: Changes that do not affect the meaning of code
  • test: Adding missing tests or correcting existing tests

Impacted Components

Which parts of the codebase does this PR touch?
Put an ✅ in the boxes that apply

  • Consensus
  • Account
  • Network
  • Geth
  • Smart Contract
  • External components
  • Not sure (Please specify below)

Checklist

Put an ✅ in the boxes once you have confirmed below actions (or provide reasons on not doing so) that

  • This PR has sufficient test coverage (unit/integration test) OR I have provided reason in the PR description for not having test coverage
  • Tested on a private network from the genesis block and monitored the chain operating correctly for multiple epochs. N/A: chain-config data and validation order only; no runtime or consensus path changes.
  • Provide an end-to-end test plan in the PR description on how to manually test it on the devnet/testnet. N/A: see above.
  • Tested the backwards compatibility. Built-in and stored configs keep resolving; stored configs hydrate the new slots from the built-in configs before the compatibility check, but only when every trio slot the stored config takes a position on already agrees with the built-in height.
  • Tested with XDC nodes running this version co-exist with those running the previous version. The fork id is byte-identical, so the handshake filter is unchanged.
  • Relevant documentation has been updated as part of this PR. docs/adr/0002-require-standard-fork-sequence.md records what this change makes stale: its migration note, the rejected option that rested on the built-in networks' shape, and the out-of-scope entry for the fallback-versus-declared-slot rejection.

@coderabbitai

coderabbitai Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 182c13be-8cb4-4b35-bc4d-bea327c560e0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gzliudan
gzliudan force-pushed the fix-config-fork-effective-blocks branch 5 times, most recently from 1860f14 to 6d03ab9 Compare October 12, 2026 06:01
@gzliudan
gzliudan requested a balanced review from Copilot October 12, 2026 06:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The consensus configuration change intentionally rejects some existing private-chain schedules and therefore warrants final human compatibility review.

0 open findings

What changed in this PR

Declares effective Constantinople, Petersburg, and Istanbul activation heights for built-in XDC networks while preserving existing execution and fork IDs.

Changes:

  • Adds explicit fork heights to network and genesis configurations.
  • Reorders fork validation and generated metadata.
  • Updates fixtures, semantic hashes, and regression coverage.
File Description
params/​config_test.go Verifies stable fork lists.
params/​config_networks.go Declares effective fork heights.
params/​config_networks_test.go Validates declarations and active forks.
params/​config_backfill_test.go Tests validation ordering and incompatibilities.
params/​config_backfill_generator.go Documents generated ordering semantics.
params/​config_backfill_generated.go Updates generated fork order.
params/​config_backfill_fields.json Moves the trio after cancellation-fee activation.
internal/​ethapi/​simulate_tipsigning_test.go Updates simulation fixture ordering.
genesis/​testnet.json Adds testnet fork declarations.
genesis/​mainnet.json Adds mainnet fork declarations.
genesis/​devnet.json Adds devnet fork declarations.
eth/​tracers/​api_tipsigning_activation_test.go Updates tracing fixture ordering.
eth/​state_accessor_tipsigning_test.go Updates state-access fixture ordering.
core/​chainconfig_equal_test.go Updates semantic hash vectors.
core/​blockchain_test.go Moves the trio in future-fork fixtures.
contracts/​utils_test.go Updates legacy contract fixture.
consensus/​tests/​engine_v2_tests/​helper.go Updates V2 legacy execution configuration.
consensus/​tests/​engine_v1_tests/​helper.go Updates V1 legacy execution configuration.
Files not reviewed (1)
  • params/config_backfill_generated.go: Generated file

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@gzliudan
gzliudan force-pushed the fix-config-fork-effective-blocks branch 3 times, most recently from 89d1e62 to c798c40 Compare October 12, 2026 10:48
@gzliudan
gzliudan force-pushed the fix-config-fork-effective-blocks branch 4 times, most recently from cd409e3 to d3fb647 Compare October 14, 2026 03:46
@gzliudan gzliudan changed the title fix(params): set constantinople/petersburg/istanbul to effective blocks fix(params): set constantinople/petersburg/istanbul to effective blocks close #2773 Oct 15, 2026
@gzliudan
gzliudan force-pushed the fix-config-fork-effective-blocks branch 2 times, most recently from f8b3770 to 2137d34 Compare October 15, 2026 11:33
@gzliudan gzliudan changed the title fix(params): set constantinople/petersburg/istanbul to effective blocks close #2773 fix(params): set constantinople/petersburg/istanbul to effective blocks, close #2773 Oct 15, 2026
@gzliudan
gzliudan force-pushed the fix-config-fork-effective-blocks branch from 2137d34 to c77d036 Compare October 15, 2026 11:34
@gzliudan gzliudan changed the title fix(params): set constantinople/petersburg/istanbul to effective blocks, close #2773 fix(params): declare constantinople/petersburg/istanbul blocks, close #2773 Oct 15, 2026
@gzliudan
gzliudan force-pushed the fix-config-fork-effective-blocks branch 4 times, most recently from ec81679 to 0b150fa Compare October 18, 2026 06:46
@gzliudan
gzliudan requested a balanced review from Copilot October 18, 2026 06:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Conditional hydration can change stored overrides’ fork IDs and historical execution rules.

2 open issues
3 resolved since last review
1 file not reviewed
  • params/config_backfill_generated.go: Generated file

Review effort: Balanced

Comment thread params/config_backfill.go Outdated
Comment thread params/config_backfill.go Outdated
@gzliudan
gzliudan force-pushed the fix-config-fork-effective-blocks branch 8 times, most recently from 872963c to 17772ec Compare October 18, 2026 10:57
@gzliudan
gzliudan requested a balanced review from Copilot October 18, 2026 11:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Stored consensus-config migration needs maintainer confirmation of private-chain compatibility, and a startup repair diagnostic remains unresolved.

1 open issue
2 resolved since last review
1 file not reviewed
  • params/config_backfill_generated.go: Generated file

Review effort: Balanced

Comment thread params/config.go Outdated
@gzliudan
gzliudan force-pushed the fix-config-fork-effective-blocks branch 2 times, most recently from 538be51 to ee21e4a Compare October 18, 2026 11:44
@gzliudan
gzliudan requested a balanced review from Copilot October 18, 2026 11:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Consensus-sensitive startup compatibility needs maintainer review, and a regression-test assertion still needs correction.

1 open issue
1 resolved since last review
1 file not reviewed
  • params/config_backfill_generated.go: Generated file

Review effort: Balanced

Comment thread params/config_backfill_test.go Outdated
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.
@gzliudan
gzliudan force-pushed the fix-config-fork-effective-blocks branch from ee21e4a to 133afc7 Compare October 18, 2026 12:00
@gzliudan
gzliudan requested a balanced review from Copilot October 18, 2026 12:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Fork-order and stored-config hydration changes require maintainer confirmation of historical and private-chain upgrade compatibility.

No open issues 🎉

1 resolved since last review
1 file not reviewed
  • params/config_backfill_generated.go: Generated file

Review effort: Balanced

@gzliudan
gzliudan merged commit 176b5c7 into XinFinOrg:dev-upgrade Oct 18, 2026
16 checks passed
@gzliudan
gzliudan deleted the fix-config-fork-effective-blocks branch October 18, 2026 12:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants