Skip to content

Commit 5a5b126

Browse files
committed
fix(core, docker, scripts): robust genesis compat handling, isolate local avalanchego, sort comm inputs
- core/genesis: - Persist upgrade bytes only when height == 0, otherwise write after compatibility check passes. - Reconcile precompile-only incompatibilities by persisting upgrade metadata and proceeding. - docker: - Only move and replace local avalanchego when it’s a standalone module (./avalanchego/go.mod), avoiding accidental source flattening. - scripts: - Sort right-hand inputs to comm in build_test.sh and lint_allowed_eth_imports.sh to eliminate “comm: input is not in sorted order”.
1 parent 4304438 commit 5a5b126

File tree

4 files changed

+85
-35
lines changed

4 files changed

+85
-35
lines changed

Dockerfile

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ RUN go mod download
1616
# Copy the code into the container
1717
COPY . .
1818

19-
# If a local avalanchego source directory is present in the repository (manual CI case),
20-
# move it outside the module root and update the replace directive to point to it so
21-
# avalanchego sources are not flattened into this module's package tree.
22-
RUN if [ -d ./avalanchego ]; then \
19+
# If a local avalanchego module is present, move it out of the module tree and
20+
# point the replace directive at it to avoid flattening into this module's packages.
21+
RUN if [ -f ./avalanchego/go.mod ]; then \
2322
mkdir -p /third_party && \
2423
mv ./avalanchego /third_party/avalanchego && \
2524
go mod edit -replace github.com/ava-labs/avalanchego=./third_party/avalanchego && \

core/genesis.go

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"errors"
3333
"fmt"
3434
"math/big"
35+
"strings"
3536
"time"
3637

3738
"github.com/ava-labs/avalanchego/vms/evm/sync/customrawdb"
@@ -177,33 +178,25 @@ func SetupGenesisBlock(
177178
return genesis.Config, common.Hash{}, &GenesisMismatchError{stored, hash}
178179
}
179180
// Get the existing chain configuration.
180-
newcfg := genesis.Config
181-
if err := newcfg.CheckConfigForkOrder(); err != nil {
182-
return newcfg, common.Hash{}, err
181+
newCfg := genesis.Config
182+
if err := newCfg.CheckConfigForkOrder(); err != nil {
183+
return newCfg, common.Hash{}, err
183184
}
184185

185186
// Read stored config into a local extras copy to avoid mutating the
186187
// caller's attached extras (which may be concurrently accessed in tests).
187188
// We'll persist the new config (including upgrade bytes) using the attached
188189
// extras below to ensure on-disk state is up to date.
189-
readExtra := *params.GetExtra(newcfg)
190+
readExtra := *params.GetExtra(newCfg)
190191
storedCfg := customrawdb.ReadChainConfig(db, stored, &readExtra)
191192
// If there is no previously stored chain config, write the chain config to disk.
192193
if storedCfg == nil {
193194
// Note: this can happen since we did not previously write the genesis block and chain config in the same batch.
194195
log.Warn("Found genesis block without chain config")
195-
customrawdb.WriteChainConfig(db, stored, newcfg, *params.GetExtra(newcfg))
196-
return newcfg, stored, nil
196+
customrawdb.WriteChainConfig(db, stored, newCfg, *params.GetExtra(newCfg))
197+
return newCfg, stored, nil
197198
}
198199

199-
// Persist the new chain config (and upgrade bytes) to disk now to avoid
200-
// spurious compatibility failures due to missing upgrade bytes on older databases.
201-
customrawdb.WriteChainConfig(db, stored, newcfg, *params.GetExtra(newcfg))
202-
203-
// Re-read stored config into a fresh extras copy for a clean comparison.
204-
compareExtra := *params.GetExtra(newcfg)
205-
storedCfg = customrawdb.ReadChainConfig(db, stored, &compareExtra)
206-
207200
// Notes on the following line:
208201
// - this is needed in coreth to handle the case where existing nodes do not
209202
// have the Berlin or London forks initialized by block number on disk.
@@ -222,24 +215,31 @@ func SetupGenesisBlock(
222215
// when we start syncing from scratch, the last accepted block
223216
// will be genesis block
224217
if lastBlock == nil {
225-
return newcfg, common.Hash{}, errors.New("missing last accepted block")
218+
return newCfg, common.Hash{}, errors.New("missing last accepted block")
226219
}
227220
height := lastBlock.NumberU64()
228221
timestamp := lastBlock.Time()
229-
if skipChainConfigCheckCompatible {
230-
log.Info("skipping verifying activated network upgrades on chain config")
231-
} else {
232-
compatErr := storedCfg.CheckCompatible(newcfg, height, timestamp)
233-
if compatErr != nil && ((height != 0 && compatErr.RewindToBlock != 0) || (timestamp != 0 && compatErr.RewindToTime != 0)) {
234-
storedData, _ := params.ToWithUpgradesJSON(storedCfg).MarshalJSON()
235-
newData, _ := params.ToWithUpgradesJSON(newcfg).MarshalJSON()
236-
log.Error("found mismatch between config on database vs. new config", "storedConfig", string(storedData), "newConfig", string(newData), "err", compatErr)
237-
return newcfg, stored, compatErr
222+
223+
// If the chain hasn't advanced past genesis (height 0), persist the new
224+
// chain config (including upgrade bytes) before compatibility checks so
225+
// subsequent restarts see the updated upgrades and do not flag them as
226+
// retroactive enables.
227+
if height == 0 {
228+
storedCfg = persistChainConfigAndReload(db, stored, newCfg)
229+
}
230+
231+
if !skipChainConfigCheckCompatible {
232+
if _, err := reconcileCompatibility(db, stored, storedCfg, newCfg, height, timestamp); err != nil {
233+
logCompatibilityMismatch(storedCfg, newCfg, err)
234+
return newCfg, stored, err
238235
}
236+
} else {
237+
log.Info("skipping verifying activated network upgrades on chain config")
239238
}
240-
// Chain config has already been written above.
241-
// Write is idempotent but not required here.
242-
return newcfg, stored, nil
239+
240+
// Persist the new chain config (including upgrade bytes) now that compatibility is verified.
241+
customrawdb.WriteChainConfig(db, stored, newCfg, *params.GetExtra(newCfg))
242+
return newCfg, stored, nil
243243
}
244244

245245
// IsVerkle indicates whether the state is already stored in a verkle
@@ -468,3 +468,54 @@ func ReadBlockByHash(db ethdb.Reader, hash common.Hash) *types.Block {
468468
}
469469
return rawdb.ReadBlock(db, hash, *blockNumber)
470470
}
471+
472+
// reconcileCompatibility checks compatibility between the stored and new chain configs.
473+
// If the only incompatibility is missing precompile upgrade metadata, it persists the
474+
// new config (including upgrade bytes) and returns a refreshed stored config view.
475+
// Otherwise, it returns the compatibility error.
476+
func reconcileCompatibility(
477+
db ethdb.Database,
478+
genesisHash common.Hash,
479+
storedCfg *params.ChainConfig,
480+
newCfg *params.ChainConfig,
481+
height uint64,
482+
timestamp uint64,
483+
) (*params.ChainConfig, error) {
484+
compatErr := storedCfg.CheckCompatible(newCfg, height, timestamp)
485+
needsRewind := compatErr != nil && ((height != 0 && compatErr.RewindToBlock != 0) || (timestamp != 0 && compatErr.RewindToTime != 0))
486+
if !needsRewind {
487+
return storedCfg, nil
488+
}
489+
if !isPrecompileUpgradeOnlyIncompatibility(compatErr) {
490+
return nil, compatErr
491+
}
492+
// Only precompile upgrade metadata differs: write upgrades and proceed.
493+
refreshed := persistChainConfigAndReload(db, genesisHash, newCfg)
494+
return refreshed, nil
495+
}
496+
497+
// persistChainConfigAndReload writes the provided chain config (including upgrade bytes)
498+
// and returns a fresh view of the stored config using a separate extras copy.
499+
func persistChainConfigAndReload(db ethdb.Database, genesisHash common.Hash, cfg *params.ChainConfig) *params.ChainConfig {
500+
customrawdb.WriteChainConfig(db, genesisHash, cfg, *params.GetExtra(cfg))
501+
refreshedExtra := *params.GetExtra(cfg)
502+
return customrawdb.ReadChainConfig(db, genesisHash, &refreshedExtra)
503+
}
504+
505+
// isPrecompileUpgradeOnlyIncompatibility returns true if the compatibility error
506+
// pertains exclusively to precompile upgrade activation metadata being absent on disk.
507+
// In such cases, persisting the new chain config (including upgrade bytes) resolves
508+
// the mismatch without requiring a rewind.
509+
func isPrecompileUpgradeOnlyIncompatibility(err *ethparams.ConfigCompatError) bool {
510+
if err == nil {
511+
return false
512+
}
513+
return strings.Contains(err.What, "PrecompileUpgrade[")
514+
}
515+
516+
// logCompatibilityMismatch emits a structured error comparing stored and new configs.
517+
func logCompatibilityMismatch(storedCfg, newCfg *params.ChainConfig, err error) {
518+
storedData, _ := params.ToWithUpgradesJSON(storedCfg).MarshalJSON()
519+
newData, _ := params.ToWithUpgradesJSON(newCfg).MarshalJSON()
520+
log.Error("found mismatch between config on database vs. new config", "storedConfig", string(storedData), "newConfig", string(newData), "err", err)
521+
}

scripts/build_test.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ do
3434
if [[ ${command_status:-0} == 0 ]]; then
3535
rm test.out
3636
exit 0
37-
else
37+
else
3838
unset command_status # Clear the error code for the next run
3939
fi
4040

4141
# If the test failed, print the output
4242
unexpected_failures=$(
4343
# First grep pattern corresponds to test failures, second pattern corresponds to test panics due to timeouts
4444
(grep "^--- FAIL" test.out | awk '{print $3}' || grep -E '^\s+Test.+ \(' test.out | awk '{print $1}') |
45-
sort -u | comm -23 - <(sed 's/\r$//' ./scripts/known_flakes.txt)
45+
sort -u | comm -23 - <(sed 's/\r$//' ./scripts/known_flakes.txt | sort -u)
4646
)
4747
if [ -n "${unexpected_failures}" ]; then
4848
echo "Unexpected test failures: ${unexpected_failures}"
@@ -56,4 +56,4 @@ do
5656
done
5757

5858
# If we reach here, we have failed all retries
59-
exit 1
59+
exit 1

scripts/lint_allowed_eth_imports.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ extra_imports=$(find . -type f \( -name "*.go" \) ! -name "mocks.go" ! -path "si
1515
grep -v 'eth\w\+ "' |
1616
grep -v '_ "' |
1717
grep -o "${libevm_regexp}" |
18-
sort -u | comm -23 - ./scripts/eth-allowed-packages.txt)
18+
sort -u | comm -23 - <(sort -u ./scripts/eth-allowed-packages.txt))
1919
if [ -n "${extra_imports}" ]; then
2020
echo "new ethereum imports should be added to ./scripts/eth-allowed-packages.txt to prevent accidental imports:"
2121
echo "${extra_imports}"

0 commit comments

Comments
 (0)