From ecbf7abd9c60f3cd8c0b51dde9592bd6936e36c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Garamv=C3=B6lgyi?= Date: Wed, 14 May 2025 15:14:38 +0200 Subject: [PATCH] fix: address race condition during EuclidV2 header chain verification --- consensus/wrapper/consensus.go | 33 +++++++++++++++++++++++++++++---- params/version.go | 2 +- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/consensus/wrapper/consensus.go b/consensus/wrapper/consensus.go index 3c5aa5cb3ea6..bfabd0ffcc30 100644 --- a/consensus/wrapper/consensus.go +++ b/consensus/wrapper/consensus.go @@ -59,6 +59,31 @@ func (ue *UpgradableEngine) VerifyHeader(chain consensus.ChainHeaderReader, head return ue.chooseEngine(header.Time).VerifyHeader(chain, header, seal) } +func waitForHeader(chain consensus.ChainHeaderReader, header *types.Header) { + hash, number := header.Hash(), header.Number.Uint64() + + // poll every 2 seconds, should succeed after a few tries + ticker := time.NewTicker(2 * time.Second) + defer ticker.Stop() + + // we give up after 2 minutes + timeout := time.After(120 * time.Second) + + for { + select { + case <-ticker.C: + // try reading from chain, if the header is present then we are ready + if h := chain.GetHeader(hash, number); h != nil { + return + } + + case <-timeout: + log.Warn("Unable to find last pre-EuclidV2 header in chain", "hash", hash.Hex(), "number", number) + return + } + } +} + // VerifyHeaders verifies a batch of headers concurrently. In our use-case, // headers can only be all system, all clique, or start with clique and then switch once to system. func (ue *UpgradableEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { @@ -124,10 +149,10 @@ func (ue *UpgradableEngine) VerifyHeaders(chain consensus.ChainHeaderReader, hea } } - // Not sure why we need this here, but without this we get err="unknown ancestor" - // at the 1st Euclid block. It seems that `VerifyHeaders` start processing the next - // header before the previous one was written into `chain`. - time.Sleep(2 * time.Second) + // `VerifyHeader` will try to call `chain.GetHeader`, which will race with `InsertHeaderChain`. + // This might result in "unknown ancestor" header validation error. + // This should be temporary, so we solve this by waiting here. + waitForHeader(chain, cliqueHeaders[len(cliqueHeaders)-1]) // Verify system contract headers. log.Info("Start EuclidV2 transition verification in SystemContract section", "startBlockNumber", systemHeaders[0].Number, "endBlockNumber", systemHeaders[len(systemHeaders)-1].Number) diff --git a/params/version.go b/params/version.go index 07af53cefdfe..2522b38243fe 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 5 // Major version component of the current release VersionMinor = 8 // Minor version component of the current release - VersionPatch = 45 // Patch version component of the current release + VersionPatch = 46 // Patch version component of the current release VersionMeta = "mainnet" // Version metadata to append to the version string )