Generator v3.1.0 - #5
Open
wanwiset25 wants to merge 59 commits into
Open
wanwiset25 wants to merge 59 commits into
wanwiset25 wants to merge 59 commits into
Conversation
- always generate chainspec
…hainspec.json mismatch
…ad of using defaults
…ot require specification of machine1
- genesis-to-chainspec: stop zeroing masternode/protector/observer rewards, carry the values from genesis.config.XDPoS.v2.allConfigs - start_xdpos.sh: take the full image ref via GENERATOR_IMAGE_VERSION instead of building it from a GENERATOR_VERSION tag - config_gen.js: default docker_image_name to the generator-v3.0.0 tag - xdpos_generator view: widen the custom version inputs to pure-input-2-3
- improve subnet generator UI
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 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. Comment |
wanwiset25
force-pushed
the
generator-v3.1.0
branch
from
September 2, 2026 12:39
0a67f88 to
24cdd08
Compare
- update default images
buildEngineParams() dropped the whole ENGINE_FORKS loop behind `if (!opts.subnet)`, on the premise that the subnet engine binds none of those names. It binds all of them: XdcSubnetChainSpecEngineParameters derives from XdcChainSpecEngineParameters and overrides only SealEngineType, so it inherits every property. The severe case is TIPUpgradeReward. Nethermind computes IsTipUpgradeRewardEnabled = (TipUpgradeReward ?? ulong.MaxValue) <= releaseStartBlock (Nethermind.Xdc/Spec/XdcChainSpecBasedSpecProvider.cs:85) so a dropped key does not mean "off", it means "never enabled". A genesis that states tipUpgradeRewardBlock would put the two clients on different reward formulas and stall the chain at the first reward checkpoint. Harmless on today's subnets, which state no tipUpgradeRewardBlock, but silent and unguarded. Verified: with those keys present in genesis, subnet mode now emits TIPUpgradeReward, TIPUpgradePenalty, TipXDCXCancellationFeeBlock, Gas50xBlock and TipSigningBlock; XDPoS output is byte-identical. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
DEFAULT_ENGINE_SUBNET emitted XDCXAddrBinary on the strength of a rename table that called it "a different name, not case". No such property exists: grepping the whole Nethermind tree at master-4e36ba0 for XDCXAddrBinary returns nothing. The bound name is XDCXAddressBinary (Nethermind.Xdc/Spec/XdcChainSpecEngineParameters.cs:33), read into the release spec at XdcChainSpecBasedSpecProvider.cs:95. So the subnet XDCX address was silently discarded and the property stayed null on every subnet chainspec the generator has produced. The rest of the rename premise goes with it: the subnet engine class overrides only SealEngineType, and MergeSignRange / BlackListHFNumber / tradingStateAddressBinary bind case-insensitively. Comment block replaced with what the code actually does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…laims (D3) The comment above the table says it "is a spread rather than a second full table" and that a subnet "only has to state what it disagrees about". It was in fact ~40 restated entries, and it broke the invariant the file header states: every transition falls back to 999999999999 so a fork genesis never mentions stays off. Ten of them fell back to real blocks instead -- eip150 at 2; eip155, eip160, eip161abc, eip161d and MaxCodeSize at 3; eip140, eip211, eip214 and eip658 at 4 -- which would have switched replay protection, state clearing, the EIP-170 cap and Byzantium on while the Go nodes ran with them off. Not reachable today: a subnet genesis states eip150Block 2, eip155Block 3, eip158Block 3 and byzantiumBlock 4, and the fallbacks were a verbatim copy of those, so genesis always won. Right by transcription, with nothing keeping it right. The spread also restores the 17 keys the table omitted entirely, so both engines now emit one key set. Verified on a real subnet genesis: 17 params keys added, no value changed, genesis block and accounts byte-identical, XDPoS output untouched. Also corrects the note above the table -- a subnet runs XDC-Subnet, not the XDPoSChain binary -- and says why the table must not be restated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The XDPoSSubnet map omitted TipXDCX, TIPXDCXMinerDisable and
TIPXDCXReceiverDisable, with a comment calling them "not used at all" on
the subnet engine. All three are inherited from
XdcChainSpecEngineParameters and bound:
- AddTransitions() adds each to the release-spec boundary set
(XdcChainSpecEngineParameters.cs:135-142)
- IsTIPXDCXMiner / IsTIPXDCXReceiver are computed from them
(XdcChainSpecBasedSpecProvider.cs:80-81)
With those three added the two maps are identical, so they collapse into
one and the per-engine selector goes away.
TipXDCXCancellationFeeBlock was covered by the D1 guard removal; it
matters twice, since TRANSITION_FORKS relies on it to activate
Constantinople and Istanbul on configs that never set istanbulBlock.
Verified: a subnet genesis stating tipXDCXCancellationFeeBlock 5 now
yields eip145Transition and eip152Transition at 5.
Also aligns blackListHFNumber -> BlackListHFNumber. Collapsing the maps
exposed that the two engines emitted the same property under different
casing; Nethermind declares it BlackListHFNumber
(XdcChainSpecEngineParameters.cs:85). Binding is case-insensitive so
behaviour is unchanged, but the drift is the same class of defect as D2.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… zero (D5)
Nethermind binds each v2Configs entry to V2ConfigParams, whose fields are
non-nullable value types with `init` accessors, and its CheckConfig only
validates that a switchRound: 0 entry exists and that no round repeats
(XdcChainSpecEngineParameters.cs:109-118). A field genesis omits is not
unset, it is 0, and nothing downstream can tell the difference.
checkV2Config() now runs per entry, after the subnet maxMasternodes
default is merged in, and names every missing field.
The required set is per engine, because the two Go clients declare
different V2Configs:
XDPoS -- XDPoSChain params/config_xdpos.go, 14 fields; a generated
XDPoS genesis states all 14.
Subnet -- XDC-Subnet params/config.go, 5 fields; a subnet genesis
states exactly those 5. Requiring the other nine would
reject every valid subnet genesis. maxMasternodes is required
on top, since SubnetMasternodesCalculator reads it and the
converter injects it.
The nine subnet-absent fields are safe at zero, and not by luck: the
subnet path reads none of them. Nethermind registers SubnetPenaltyHandler
(XdcSubnetModule.cs:32), which uses the hardcoded
XdcConstants.MinimumMinerBlockPerEpoch = 1 and never reads
LimitPenaltyEpoch or MinimumSigningTx; XDC-Subnet hardcodes the same 1.
The reward and protector/observer cap fields are read only by
XdcRewardCalculator, and XDC-Subnet has no protector/observer tiers.
main() now catches a translate() failure and reports it as a message and
exit 1 instead of an unhandled stack trace -- this runs in a container
where the trace is all an operator would see.
Verified: both real genesis files still convert; a subnet genesis
converted without --subnet now fails naming all nine fields, which is the
forgot-the-flag case that previously produced a silently wrong chainspec.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"0xundefined" (D6) normalizeAddress() returned '0x' + String(addr), so a genesis missing both spellings of the foundation wallet produced the literal foundationWalletAddr = "0xundefined", exit 0, no warning. The node then died at load with System.IO.InvalidDataException: Error when loading chainspec (hex string of odd length) which names neither the field nor the file. normalizeAddress() now passes undefined/null through, and buildEngineParams() throws naming the field. The `||` in the shared object also becomes pick(), so an address of 0x0 -- falsy as a string only if empty, but the pattern is wrong either way -- cannot fall through to the second spelling. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nethermind parses the genesis masternode set out of genesis.extraData only when SwitchBlock == 0; otherwise it reads the engine param genesisMasternodes, which defaults to an empty array (XdcChainSpecBasedSpecProvider.cs:101-112). The converter has no source for that key and never emitted it. Generated deployments were safe by accident, since genesis states switchBlock: 0. Set it to anything else and the node boots reporting the correct genesis hash with no masternodes and no error. buildEngineParams() now throws rather than emit a chainspec that looks right and has no validator set. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Osaka warnings work, but translate() runs in-process inside the container-manager, so console.error landed in the container log while the operator's page said "Config generation success". check-chainspec.sh re-runs the converter in the foreground, which was the only way one was ever seen. translate() now takes an optional opts.warnings array and pushes into it; with no array it still writes to console.error, so the CLI and check-chainspec.sh are unchanged. generate() and generateXdpos() pass a sink and return it as a third element, and both submit templates render the warnings alongside the success message. Verified: the CLI still prints to stderr; the sink captures with the console silent; both templates render a warning list and omit the section entirely when there is none. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n (D9) check-chainspec.sh documents that the engine "is recorded in gen.env when the deployment is generated". Only the Subnet path wrote it. The check happened to be right anyway, because no flag is the XDPoS default -- the safety was the default, not the record. That left a live failure mode: the script read the environment first, so $ CHAINSPEC_ENGINE=XDPoSSubnet ./scripts/check-chainspec.sh MISMATCH: chainspec.json does not match genesis.json # exit 3 on a working XDPoS network, and without --dry-run it archives the correct chainspec and replaces it with a subnet one -- engine renamed, fork keys stripped. An exported value left over from another deployment in the same shell is enough. Two changes: genGenXdposEnv() now records CHAINSPEC_ENGINE=XDPoS, and the script prefers the recorded value over the environment, saying so when they disagree. The environment is still the fallback for a deployment generated before this, whose gen.env records nothing. Verified all five cases: XDPoS and Subnet deployments each resolve correctly with no env, with a stray opposing env (ignored, with a note), and a legacy gen.env with no record still honours the environment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… XDC (D10) The per-node env file set NETHERMIND_NETWORKCONFIG_FILTERPEERSBYRECENTIP. Nethermind's own XDC config sets FilterDiscoveryNodesByRecentIp instead (Nethermind.Runner/configs/xdc.json). Both properties exist on NetworkConfig and both default to true, so the discovery-side filter stayed on regardless -- the setting was not doing what it looked like it did. Upstream also carries DiscoveryVersion V4, EnableEnrDiscovery false and DisableDiscV4DnsFeeder true, none of which were set here. Both flags plus the three discovery settings now live in xdc-nmc.json and xdc-nmc-subnet.json, and the per-node env var is dropped from gen_xdpos.js and gen_env.js -- it is a shared setting, not a per-node one. (The subnet path was setting it to true, i.e. the default, so it had no effect at all.) Alignment work rather than a bug fix: a 3-node run converged on discv4 anyway. TargetBlockGasLimit values are untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every generated Nethermind node ran at debug: tens of thousands of lines in twenty minutes, for a node that is working correctly. Applied to both generators. The review named only gen_xdpos.js, but gen_compose.js:53 carried the same flag for subnet Nethermind nodes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
header properly (D12) The file header already argued for dropping homesteadBlock, byzantiumBlock and eip158Transition because Nethermind binds none of them. The same test, run against Nethermind.Specs and Nethermind.Xdc at master-4e36ba0, fails for 26 more keys. engine (2): period, rewardCheckpoint. Neither exists on XdcChainSpecEngineParameters. The mine period comes from v2Configs[].minePeriod, which is bound. genesis (5): nonce, mixHash, coinbase, number, gasUsed. ChainSpecGenesisJson declares none of them -- ChainSpecLoader.cs:356-357 reads the nonce and mixHash from Genesis.Seal.Ethereum, and :369 reads the beneficiary from Genesis.Author. These are now emitted as seal.ethereum and author. number and gasUsed are dropped: genesis is always block 0 with no gas used, and the loader hardcodes the number. params (19): six with no ChainSpecParamsJson property in any form (eip1234, eip2718, eip3554, eip4399, eip6049, eip7516) and thirteen declared only as eipNNNNTransitionTimestamp, which a block-numbered value cannot bind. Audited by matching every emitted key against the binding class rather than by hand; the other 42 are genuinely bound. A comment records the finding so they are not re-added. The stated rationale for emitting them -- "impossible to switch on for Nethermind alone by accident" -- does not survive nothing reading them. eip1234Transition is the illustration: IsEip1234Enabled derives from the Constantinople block, i.e. eip145Transition, which this converter defaults to 0, so it was on from block 0 regardless of the 999999999999 beside it. The genesis five had teeth. It cost nothing so far only because a generated genesis has all five at their defaults -- which is why the hashes matched -- but a non-zero coinbase, nonce or mixHash would have silently given Nethermind a different genesis block. Genesis hash re-verified: the emitted nonce (0), mixHash (zero) and author (zero address) are exactly the values ChainSpecLoader was already defaulting to, so the header is byte-identical and existing node data directories stay valid. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…k name (D13) Two unrelated bits of the same scaffolding. network_mode: "host" was set on every Go node in gen_xdpos.js and gen_compose.js, and injectNetworkConfig deleted it again from every service before writing the compose file, assigning a bridge IP instead. It never reached a generated deployment. Removed at the source, along with the now-redundant delete, so the next reader does not reason from it. exec.js passed no --name to translate(), so every chainspec was called "xdpos-chain" while gen.env recorded the operator's NETWORK_NAME. Both generate paths now pass it, and check-chainspec.sh reads NETWORK_NAME back out of gen.env. Both halves have to land together: verified that a chainspec generated with --name my-subnet passes the check when the name is read back, and reports a one-line name mismatch (exit 3, and a rewrite without --dry-run) when it is not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tip2019Block to 1 (D14)
These two are the only entries in DEFAULT_ENGINE_SUBNET that switch a
fork ON where genesis states nothing, and the only justification was
"match a working Nethermind+Go subnet deployment". TipTrc21Fee is read by
the transaction-execution path, so that was the weakest claim in the
file.
Both values are correct. Checked against XinFinOrg/XDC-Subnet, the client
a subnet actually runs:
tip2019Block: 1 -- Nethermind: IsTIP2019 = TIP2019Block <=
releaseStartBlock, on from block 1. XDC-Subnet hardcodes
common.TIP2019Block = 1, isForked() from block 1. Agree.
TipTrc21Fee: 1 -- Nethermind: (TipTrc21Fee ?? MaxValue) <=
releaseStartBlock, on from block 1. XDC-Subnet hardcodes
common.TIPTRC21Fee = 0 but tests it strictly, `BlockNumber.Cmp(...)
> 0` (core/state_transition.go:271), so live from block 1 as well.
Agree.
No value changes. The comment now carries the reason for each, and warns
against "correcting" the 1 to 0 to match the Go constant -- the Go test
is strict, so 0 would enable it a block early.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follow-up to D13, which introduced this. gen.env is written as
NETWORK_NAME=${input["text-subnet-name"]}, so an input without that field
-- /submit_preconfig posts req.body straight through -- records the
literal string "undefined". translate() meanwhile saw JS undefined and
fell back to DEFAULT_CHAIN_NAME, so the chainspec is called
"xdpos-chain".
Now that check-chainspec.sh reads NETWORK_NAME back, those two disagree:
the check re-translates with --name undefined, sees a one-line name
difference, and archives and rewrites a correct chainspec on every run.
That is precisely the failure the --name plumbing exists to prevent.
The script now ignores an empty or "undefined" value, which also covers
deployments already generated with one recorded, and exec.js writes an
empty value rather than "undefined" going forward.
Verified: "undefined" and "" yield no flag; a real name and a name
containing spaces are both passed as a single argument.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two stale claims in the mapping block. period and rewardCheckpoint were still listed as mapped to engine params; D12 stopped emitting them because neither is a property of XdcChainSpecEngineParameters. The v2Configs TODO claimed the masternode/protector/observer reward amounts are "zeroed, not carried". They are carried verbatim: puppeth computes them (calcMasternodeRewards in cmd/puppeth/wizard_genesis.go) and writes them into V2.CurrentConfig, which aliases AllConfigs[0], and the converter passes the whole entry through. A recent genesis carries masternodeReward 63.42, minimumMinerBlockPerEpoch 5, limitPenaltyEpoch 5 and minimumSigningTx 30. Replaced with what the code does, including the new checkV2Config() guard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
D10 moved this setting out of the per-node env files and into the shared Nethermind configs, but set it to false in both. That silently changed subnet behaviour: the per-node env had it true there, and false only on the XDPoS path. Restores the original per-engine values, now stated where the rest of the network settings live: xdc-nmc.json (XDPoS) false -- was false in gen_xdpos.js xdc-nmc-subnet.json (Subnet) true -- was true in gen_env.js FilterDiscoveryNodesByRecentIp stays false on both. That is the flag upstream's own XDC config sets, and the one D10 was about: the peer-side filter and the discovery-side filter are separate properties, and only the discovery-side one was never being configured at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The file had accumulated a 150-line header that restated the mapping tables key by key, plus comments orphaned by the D12 key removals. The duplicated header had already drifted twice and needed fixing twice. Structural changes: - Header 150 -> 64 lines. The key-by-key mapping lists are gone; the tables are the mapping and are now pointed at rather than copied. What remains is the four things the tables cannot say for themselves: the two Go clients, XDPoSChain's non-canonical fork schedule, the two "off" conventions, and the silently-ignored-key hazard. - buildEngineParams(): the D6/D7 guards had been spliced into the middle of the comment explaining the default-table spread, cutting that explanation in half. Guards moved to the top with their own note; the spread explanation is whole again and the three merge passes are numbered. - Removed comments left dangling by D12: eip1234's rationale (sitting above eip1283) and eip2718's (above eip2929), plus an empty "// Osaka" heading and a misplaced beacon-chain aside. - Compressed the DEFAULT_ENGINE_SUBNET and checkV2Config notes roughly in half without dropping the evidence. Corrections found while checking the claims against source: - eip2028: the comment said xdc-testnet.json "agrees: 999999999999". It omits the key entirely. Corrected. - Header rule on fallbacks said every params transition falls back to 999999999999. Eleven sit at 0, for forks the chain genuinely runs from block 0. Corrected. - Reference spec path was /nethermind/chainspec/; it is src/Nethermind/Chains/. Corrected. - Dropped the stale nethermindeth/nethermind:xdc-fixes references; the binding claims are checked against master-4e36ba0, the image the deployment pins. Everything kept was re-verified against source: InitialBaseFee 12500000000, MaxCodeSize 24576/32768, MergeSignRange 15, RangeReturnSigner 150, TxDataNonZeroGas 68 with EIP2028's 16 unreferenced, IsIstanbul's OR over TIPXDCXCancellationFeeBlock, IsEIP1559, newBerlinInstructionSet's commented-out enable2929, newEip1559InstructionSet = Shanghai + 2929/3529/3860, London = +3198 only, Shanghai = +3855 only, EIP-3541's 0xEF check, 3860 via IntrinsicGas, 2929 /2930 via StateDB.Prepare and the txpool type gate, 3651's warm coinbase, 1283's Constantinople-and-not-Petersburg window, EIP-2935 via ProcessParentBlockHash, and Apothem's 61290000/71550000/83600000 splits. Comments only: converter output is byte-identical on both engines, all genesis files still convert and round-trip, and all three guards fire. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nspec generation generate() has gated chainspec failure on nethermindCount since the subnet client was integrated (3deb7b5) -- an all-Go subnet never reads the chainspec, so a failure there must not block the deployment. generateXdpos() never got the same gate and failed the deployment unconditionally, even with NUM_NETHERMIND=0. This pairs with 7028294, which stopped docker-up.sh running the pre-check when no service mounts /work/chainspec.json. That half was right: verified on the two generated deployments in the tree, an all-Go compose resolves to zero chainspec targets and skips the check, while one with two Nethermind nodes resolves to two and runs it. Generation was the half still able to stop an all-Go network over a file nothing mounts. generateXdpos() now counts Nethermind nodes exactly as genGenXdposEnv records NUM_NETHERMIND -- keyed on customversion-checkbox, where the subnet path keys on customclient-checkbox -- and only fails when one was actually requested. When it is not fatal, the failure now goes into the warnings array on both paths instead of the container log alone, so the operator sees why there is no chainspec on the success page rather than discovering it when they later add a Nethermind node. Not reachable with a normally generated genesis -- every genesis in the tree still converts -- but D5, D6 and D7 added three deliberate throws to the converter, so the asymmetry had more ways to bite than before. Verified: both paths now resolve identically across five input shapes (no checkbox, checkbox with count 0, count set but checkbox off, and counts 1 and 3), and the non-fatal warning renders on both success pages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…firmation
Stops the containers, then deletes every xdcchain* data directory, so the
next docker-up.sh starts the chain from block 0. Keys, genesis.json,
chainspec.json and the env files are left alone.
It does nothing until the operator types exactly Y -- lowercase y, yes
and everything else cancel without stopping or deleting anything. Before
asking, it prints the deployment path, the network name, the chain id,
the chainspec name, the current head block, and the absolute path and
size of every directory it would remove, plus the chain data and whole
deployment totals, so the answer is given against what will actually go.
The head block is cross-checked against the chain id. RPC ports are
host-wide, so a node answering on 8545 need not belong to this
deployment; only nodes whose eth_chainId matches genesis are counted, and
any that do not are reported as a warning to check the directory. Without
that, an unrelated chain on the same machine reads as this one's height.
Deleting by glob, so the target is pinned rather than assumed. The data
directories are defined as exactly one level above the script:
- the script's own directory is resolved with pwd -P and must be named
"scripts", so a copy left anywhere else refuses outright
- the deployment root is its parent and must hold both
docker-compose.yml and genesis.json; never / or $HOME
- targets must be direct children of that root, real directories, and
still inside it once resolved -- a symlinked xdcchain* is listed under
"Not touching" and never followed
- everything is deleted by absolute path, not a relative glob
- if stopping the containers fails, nothing is deleted
So it operates on its own deployment whatever the working directory is.
Works on both deployment shapes: an XDPoS deployment gets docker-down.sh
at its root and that is used; a subnet one does not, so it falls back to
compose directly, taking the single profile when there is one and
requiring it as an argument when there are several.
Copied into generated deployments by gen.js and gen_xdpos.js.
Verified against fake deployments of both shapes: n / y / yes all cancel
with the data intact; Y proceeds; a failing docker-down deletes nothing
and exits with its code; no chain data exits 0 before prompting; a
missing docker-compose.yml or a script outside scripts/ refuses; a
symlink pointing outside is skipped with its target untouched; and run by
absolute path from a directory that itself contained xdcchain1 and
xdcchain9, it removed the deployment's directory and left both decoys.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reverts D11 (52a4dfa), which lowered the Nethermind nodes to --log=info, and drops the per-node NMC<i>_LOG_LEVEL mechanism built on top of it. Nethermind exposes the level only through --log, a command-line option with no config property behind it, and Init.LogRules, whose value is Pattern:Level pairs and which throws at startup if given a bare level. Neither makes a good per-node knob in a generated deployment, so the compose command carries the level directly again, as it did originally. The D11 finding still stands -- debug is tens of thousands of lines in twenty minutes on a healthy node -- so change this line to info once the current debugging is done. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two bugs, both hit on the first real subnet run. It demanded a profile. A subnet has three (machine1, services, subswap), so it refused with "name the one to reset" and did nothing. Naming one would have been wrong anyway: `down` with a single --profile leaves the other profiles' containers running, and a running node whose data directory has just been deleted keeps writing into a path that no longer exists. It now takes no argument and stops every configured profile, passing one --profile flag per entry from `config --profiles` -- verified that plain `down` with no --profile leaves profiled services up, so they have to be named. It did not export HOSTPWD. A subnet docker-compose.yml interpolates it into every volume path, so each compose call printed "The HOSTPWD variable is not set" once per service and resolved the paths to /xdcchain1 rather than the deployment's. It is now exported as the deployment root, which is what it means. The docker-down.sh branch goes with the profile argument: that script only resolves a profile and calls compose, which this now does directly and for every profile, so both deployment shapes take one path. Also corrects two messages that told the operator to restart with ./docker-up.sh, which a subnet deployment does not have -- it starts from the wizard. Verified on a subnet-shaped deployment with four containers across three profiles: all four stopped, both data directories removed, genesis.json, chainspec.json and gen.env kept, and no HOSTPWD warning. The single-profile XDPoS shape still resets with no argument, and the cancel-unless-Y path is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tches gen hardcoded the bootnode's enode id on the claim that "the bootnode container always starts from the same bootnode.key". No key was ever shipped. start-bootnode.sh only writes bootnode.key when it is handed a key through PRIVATE_KEY or PRIVATE_KEY_FILE, so it fell through to `bootnode -genkey`, and that key lands in the container's /work, which is not the mounted volume -- a fresh identity on every recreate. Every node was therefore configured with an enode nothing answered to. On a four-node subnet each one logged "Online bootnodes: 0" every 31s and never validated the bootnode, so the mesh was whatever the nodes caught from NEIGHBORS in the first seconds: node1 and node2 each held a single link to node4, and node3 lost its three entries within 30s and sat at block 0 with no peers. Being a validator, its leader slot then timed out every fourth round. Generate the key in config_gen, write it to bootnodes/bootnode.key, and derive the enode id from it -- an enode id is the uncompressed secp256k1 public key minus its 0x04 prefix -- so the two cannot drift. The bootnode reads the key back through PRIVATE_KEY_FILE, set on the service rather than in common.env, which the relayer, stats and frontend containers also read and where PRIVATE_KEY is a generic enough name to be picked up. BOOTNODE_PK reuses an existing bootnode identity. gen_xdpos carried the same hardcoded enode in two more places, for bootnodes.list and for each masternode env file; both now go through gen_env.bootnodeEnode, which is exported for that. Verified against go-ethereum's own `bootnode -writeaddress`, not just ethers: on the subnet path the written key derives to exactly the enode in all four subnet*nmc.env, on the XDPoS path to the one in bootnodes.list and masternode*nmc.env, and BOOTNODE_PK=0x662e81df... reproduces e902b89c... identically across runs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resetting repeatedly during a peering investigation means answering the same prompt every time, and `./scripts/reset-chain.sh -Y` looked like it did something: the script took no arguments and ignored what it was given, so it printed the summary and sat at the prompt anyway. -y, -Y or --yes skips the prompt. -Y as well as -y because Y is the literal reply the prompt asks for, so it is the spelling a hand reaches for first. An unrecognised argument is now refused with exit 2 rather than ignored, which is what made the original mistake invisible, and the parsing runs ahead of every guard so a typo costs a millisecond instead of a full summary. --yes replaces the prompt and nothing else. The deployment-shape check, the / and $HOME refusal, the docker-compose.yml and genesis.json requirement, and the symlink and resolves-outside-root skipping all stay unconditional, and the summary is still printed -- it is the record of what was destroyed. The one exception is the foreign-chain notice. It fires when a node answering on this deployment's ports reports a different chain id, and it is addressed to whoever is reading. Unattended there is no one, so --yes stops on it instead of rolling past, with no override flag: the recourse is an interactive run, which shows the same notice and lets it be judged. Deleting is still bounded to $root/xdcchain* either way, so this is about not discarding a signal, not about what is at risk. A piped `echo Y` keeps working, since that is the escape hatch people already have. Reaching EOF without an answer now names --yes rather than reporting a bare "Cancelled". Verified on a throwaway deployment: unknown argument refused with exit 2; -y, -Y and --yes each reset and exit 0; piped Y unchanged; piped n and EOF both leave the data in place with exit 1; a live node of another chain id on 8545 makes --yes exit 2 having stopped and deleted nothing; and the script moved out of scripts/ still refuses under --yes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The right value depends on which clients a deployment runs, so a static one in the xdc-nmc*.json that every node mounts alike was bound to be wrong for half the deployments -- and both files were: xdc-nmc-subnet.json true correct mixed, strands an all-Nethermind net xdc-nmc.json false correct all-Nethermind, churns when mixed All Nethermind needs false. The nodes boot together, learn each other at once through the bootnode and dial simultaneously; the collisions leave every retry suppressed for the filter's five-minute window, and the peer manager sits on "EligibleCandidates: 2, Tried: 0" with nodes at zero peers. On a four-node subnet needing three to agree that is a stalled chain, observed twice: blocks [21,21,21,0] peers [1,2,1,0] on one run, and stuck at block 1 with two nodes isolated on another. Upstream Nethermind disables the filter for Hive and the E2E sync tests, which are the same shape -- several nodes, one host, private addresses. Mixed with the Go client needs true, which is the opposite of what the mesh argument above suggests. The Go client has no such filter and redials without pause, so an unfiltered Nethermind tears those sessions down and rebuilds them continuously: 451 failed connections per second, "snappy: corrupt input" on every Go-to-Nethermind handshake, the Go node's own admin.peers holding no Nethermind peer in 400 consecutive samples, and 10 MB of log in fourteen minutes. The chain still ran, on the churn rather than on any session. Leaving the filter on lets an established session stay up instead. So it moves to the per-node env, computed from num_nethermind, which is where it lived before D10 folded it into the shared config; the static value is dropped from both JSON files so there is one source of truth and nothing silently overridden. Underneath this is an interop bug -- devp2p snappy framing between xdcsubnets v0.3.2 and Nethermind on the xdpos2 capability, while Nethermind-to-Nethermind on xdc165 is unaffected. Until that is fixed this setting decides only how hard the two clients retry past it. Verified by generating both paths at three mixes: 4 of 4 gives false, 2 of 4 gives true, 0 of 4 emits no Nethermind env file at all, and no copied config still carries the key. The mixed value is the one tested at 2 of 4, where neither client group reaches quorum alone, so the chain advances only if votes really do cross between them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This branch was successfully deployed
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.
### Subnet Deployment v3.1.0