engine_v2, params: fix unsynchronized reads of V2.CurrentConfig, close XFN-53 (#1642)

This commit is contained in:
Daniel Liu 2025-11-03 17:21:54 +08:00 committed by GitHub
parent 33c09226ba
commit 5a26279c1c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 23 additions and 7 deletions

View file

@ -150,14 +150,15 @@ func (x *XDPoS_v2) UpdateParams(header *types.Header) {
x.config.V2.UpdateConfig(uint64(round))
// Setup timeoutTimer
duration := time.Duration(x.config.V2.CurrentConfig.TimeoutPeriod) * time.Second
err = x.timeoutWorker.SetParams(duration, x.config.V2.CurrentConfig.ExpTimeoutConfig.Base, x.config.V2.CurrentConfig.ExpTimeoutConfig.MaxExponent)
currentConfig := x.config.V2.GetCurrentConfig()
duration := time.Duration(currentConfig.TimeoutPeriod) * time.Second
err = x.timeoutWorker.SetParams(duration, currentConfig.ExpTimeoutConfig.Base, currentConfig.ExpTimeoutConfig.MaxExponent)
if err != nil {
log.Error("[UpdateParams] set params failed", "err", err)
}
// avoid deadlock
go func() {
x.minePeriodCh <- x.config.V2.CurrentConfig.MinePeriod
x.minePeriodCh <- currentConfig.MinePeriod
}()
}
@ -261,10 +262,11 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er
}
// Initial timeout
log.Warn("[initial] miner wait period", "period", x.config.V2.CurrentConfig.MinePeriod)
currentConfig := x.config.V2.GetCurrentConfig()
log.Warn("[initial] miner wait period", "period", currentConfig.MinePeriod)
// avoid deadlock
go func() {
x.minePeriodCh <- x.config.V2.CurrentConfig.MinePeriod
x.minePeriodCh <- currentConfig.MinePeriod
}()
// Kick-off the countdown timer

View file

@ -324,7 +324,7 @@ func (x *XDPoS_v2) OnCountdownTimeout(time time.Time, chain interface{}) error {
}
x.timeoutCount++
if x.timeoutCount%x.config.V2.CurrentConfig.TimeoutSyncThreshold == 0 {
if x.timeoutCount%x.config.V2.GetCurrentConfig().TimeoutSyncThreshold == 0 {
syncInfo := x.getSyncInfo()
log.Info("[OnCountdownTimeout] Timeout sync threshold reached, send syncInfo message", "QC round", syncInfo.HighestQuorumCert.ProposedBlockInfo.Round, "QC num", syncInfo.HighestQuorumCert.ProposedBlockInfo.Number, "QC sigs", len(syncInfo.HighestQuorumCert.Signatures), "TC round", syncInfo.HighestTimeoutCert.Round, "TC sigs", len(syncInfo.HighestTimeoutCert.Signatures))
x.broadcastToBftChannel(syncInfo)

View file

@ -560,7 +560,7 @@ func (v2 *V2) Description(indent int) string {
banner += fmt.Sprintf("%s- SwitchEpoch: %v\n", prefix, v2.SwitchEpoch)
banner += fmt.Sprintf("%s- SwitchBlock: %v\n", prefix, v2.SwitchBlock)
banner += fmt.Sprintf("%s- SkipV2Validation: %v\n", prefix, v2.SkipV2Validation)
banner += fmt.Sprintf("%s- %s", prefix, v2.CurrentConfig.Description("CurrentConfig", indent+2))
banner += fmt.Sprintf("%s- %s", prefix, v2.GetCurrentConfig().Description("CurrentConfig", indent+2))
return banner
}
@ -625,6 +625,20 @@ func (v2 *V2) UpdateConfig(round uint64) {
v2.CurrentConfig = v2.AllConfigs[index]
}
// GetCurrentConfig returns a opy of the current config, it assumes v2 is not nil
func (v2 *V2) GetCurrentConfig() *V2Config {
v2.lock.RLock()
defer v2.lock.RUnlock()
if v2.CurrentConfig == nil {
return nil
}
// avoid CurrentConfig is changed by other goroutines
cpyConfig := *v2.CurrentConfig
return &cpyConfig
}
func (v2 *V2) Config(round uint64) *V2Config {
configRound := round
var index uint64