fix(params): accept legacy foudationwalletaddr in chain config, fix #2063 (#2141)

Add backward-compatible XDPoSConfig JSON decoding for the legacy key
"foudationWalletAddr" introduced before PR #2063 renamed it to
"foundationWalletAddr".

Without this compatibility layer, old on-disk chain configs are decoded with a
zero FoundationWalletAddr, causing XDPoSConfigEqual mismatch and startup rewind
("mismatching XDPoS not equal in database").

This patch:
- Implements custom UnmarshalJSON for XDPoSConfig that reads both keys.
- Prefers foundationWalletAddr when both keys are present.
- Keeps existing behavior for all other fields.
- Adds regression tests for legacy-key decoding and precedence.

Validation:
- go test ./params/...
This commit is contained in:
Daniel Liu 2026-03-07 06:37:24 +08:00 committed by GitHub
parent 4f599282b3
commit ed95075f03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 55 additions and 0 deletions

View file

@ -18,6 +18,7 @@ package params
import (
"cmp"
"encoding/json"
"fmt"
"maps"
"math/big"
@ -560,6 +561,40 @@ type XDPoSConfig struct {
V2 *V2 `json:"v2"`
}
// UnmarshalJSON supports both the current and legacy typo-ed JSON key for
// foundation wallet address to keep old on-disk chain configs compatible.
func (c *XDPoSConfig) UnmarshalJSON(data []byte) error {
type xdpJSON struct {
Period uint64 `json:"period"`
Epoch uint64 `json:"epoch"`
Reward uint64 `json:"reward"`
RewardCheckpoint uint64 `json:"rewardCheckpoint"`
Gap uint64 `json:"gap"`
FoundationWalletAddr common.Address `json:"foundationWalletAddr"`
LegacyFoudationWalletAddr common.Address `json:"foudationWalletAddr"`
SkipV1Validation bool `json:"SkipV1Validation"`
V2 *V2 `json:"v2"`
}
var decoded xdpJSON
if err := json.Unmarshal(data, &decoded); err != nil {
return err
}
c.Period = decoded.Period
c.Epoch = decoded.Epoch
c.Reward = decoded.Reward
c.RewardCheckpoint = decoded.RewardCheckpoint
c.Gap = decoded.Gap
c.FoundationWalletAddr = decoded.FoundationWalletAddr
if c.FoundationWalletAddr == (common.Address{}) && decoded.LegacyFoudationWalletAddr != (common.Address{}) {
c.FoundationWalletAddr = decoded.LegacyFoudationWalletAddr
}
c.SkipV1Validation = decoded.SkipV1Validation
c.V2 = decoded.V2
return nil
}
type V2 struct {
lock sync.RWMutex // Protects the signer fields

View file

@ -17,10 +17,12 @@
package params
import (
"encoding/json"
"math/big"
"reflect"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/stretchr/testify/assert"
)
@ -150,3 +152,21 @@ func TestSwitchEpoch(t *testing.T) {
epoch = config.Epoch
assert.Equal(t, config.V2.SwitchEpoch, config.V2.SwitchBlock.Uint64()/epoch)
}
func TestXDPoSConfigUnmarshalLegacyFoundationWalletAddr(t *testing.T) {
const raw = `{"period":2,"epoch":900,"reward":5000,"rewardCheckpoint":900,"gap":450,"foudationWalletAddr":"xdc746249c61f5832c5eed53172776b460491bdcd5c"}`
var cfg XDPoSConfig
err := json.Unmarshal([]byte(raw), &cfg)
assert.NoError(t, err)
assert.Equal(t, common.HexToAddress("xdc746249c61f5832c5eed53172776b460491bdcd5c"), cfg.FoundationWalletAddr)
}
func TestXDPoSConfigUnmarshalFoundationWalletAddrPrecedence(t *testing.T) {
const raw = `{"period":2,"epoch":900,"reward":5000,"rewardCheckpoint":900,"gap":450,"foudationWalletAddr":"xdc746249c61f5832c5eed53172776b460491bdcd5c","foundationWalletAddr":"xdc92a289fe95a85c53b8d0d113cbaef0c1ec98ac65"}`
var cfg XDPoSConfig
err := json.Unmarshal([]byte(raw), &cfg)
assert.NoError(t, err)
assert.Equal(t, common.HexToAddress("xdc92a289fe95a85c53b8d0d113cbaef0c1ec98ac65"), cfg.FoundationWalletAddr)
}