Skip to content

Commit 31d387c

Browse files
committed
refactor: remove upgrade* functions from system contracts
Enforce code-only upgrade principle: hardfork upgrades now only deploy new contract bytecode without modifying on-chain state. State initialization is restricted to v1 (genesis) only, preserving governance-modified values.
1 parent 3a63463 commit 31d387c

13 files changed

+22
-907
lines changed

systemcontracts/coin_adapter.go

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -168,86 +168,3 @@ func initializeCoinAdapter(coinAdapterAddress common.Address, param map[string]s
168168
return sp, nil
169169
}
170170

171-
// upgradeCoinAdapter performs a migration-style upgrade for NativeCoinAdapter.
172-
// Only parameters present in param are written. Missing keys are skipped,
173-
// so existing on-chain values (masterMinter, name, symbol, etc.) are preserved.
174-
func upgradeCoinAdapter(coinAdapterAddress common.Address, param map[string]string) ([]params.StateParam, error) {
175-
sp := make([]params.StateParam, 0)
176-
177-
if masterMinterStr, ok := param[COIN_ADAPTER_PARAM_MASTER_MINTER]; ok {
178-
sp = append(sp, params.StateParam{
179-
Address: coinAdapterAddress,
180-
Key: common.HexToHash(SLOT_COIN_ADAPTER_MASTER_MINTER),
181-
Value: common.BytesToHash(common.HexToAddress(masterMinterStr).Bytes()),
182-
})
183-
}
184-
185-
if mintersStr, ok := param[COIN_ADAPTER_PARAM_MINTERS]; ok && len(mintersStr) > 0 {
186-
minters := splitAndTrim(mintersStr, ",")
187-
minterAllowedAmounts := make([]*big.Int, len(minters))
188-
if minterAllowedStr, ok := param[COIN_ADAPTER_PARAM_MINTER_ALLOWED]; ok && len(minterAllowedStr) > 0 {
189-
minterAllowed := splitAndTrim(minterAllowedStr, ",")
190-
if len(minters) != len(minterAllowed) {
191-
return nil, fmt.Errorf("`systemContracts.nativeCoinAdapter.params`: the number of minters and minterAllowedAmounts must be the same")
192-
}
193-
for i, allowed := range minterAllowed {
194-
allowedAmount, ok := new(big.Int).SetString(allowed, 10)
195-
if !ok {
196-
return nil, fmt.Errorf("`systemContracts.nativeCoinAdapter.params`: invalid minterAllowed, must be decimal")
197-
}
198-
minterAllowedAmounts[i] = allowedAmount
199-
}
200-
}
201-
mintersSlot := common.HexToHash(SLOT_COIN_ADAPTER_MINTERS)
202-
minterAllowedSlot := common.HexToHash(SLOT_COIN_ADAPTER_MINTER_ALLOWED)
203-
for i, minter := range minters {
204-
minterAddress := common.HexToAddress(minter)
205-
sp = append(sp, params.StateParam{
206-
Address: coinAdapterAddress,
207-
Key: CalculateMappingSlot(mintersSlot, minterAddress),
208-
Value: common.BytesToHash([]byte{1}),
209-
})
210-
if minterAllowedAmounts[i] != nil {
211-
sp = append(sp, params.StateParam{
212-
Address: coinAdapterAddress,
213-
Key: CalculateMappingSlot(minterAllowedSlot, minterAddress),
214-
Value: common.BigToHash(minterAllowedAmounts[i]),
215-
})
216-
}
217-
}
218-
}
219-
220-
if coinName, ok := param[COIN_ADAPTER_PARAM_NAME]; ok && len(coinName) > 0 {
221-
for slot, value := range EncodeBytesToSlots(common.HexToHash(SLOT_COIN_ADAPTER_NAME), []byte(coinName)) {
222-
sp = append(sp, params.StateParam{Address: coinAdapterAddress, Key: slot, Value: value})
223-
}
224-
}
225-
226-
if symbol, ok := param[COIN_ADAPTER_PARAM_SYMBOL]; ok && len(symbol) > 0 {
227-
for slot, value := range EncodeBytesToSlots(common.HexToHash(SLOT_COIN_ADAPTER_SYMBOL), []byte(symbol)) {
228-
sp = append(sp, params.StateParam{Address: coinAdapterAddress, Key: slot, Value: value})
229-
}
230-
}
231-
232-
if decimalsStr, ok := param[COIN_ADAPTER_PARAM_DECIMALS]; ok && len(decimalsStr) > 0 {
233-
decimals, err := strconv.ParseUint(decimalsStr, 10, 8)
234-
if err != nil {
235-
return nil, fmt.Errorf("`systemContracts.nativeCoinAdapter.params.decimals`: %w", err)
236-
}
237-
sp = append(sp, params.StateParam{
238-
Address: coinAdapterAddress,
239-
Key: common.HexToHash(SLOT_COIN_ADAPTER_DECIMALS),
240-
Value: common.BytesToHash([]byte{uint8(decimals)}),
241-
})
242-
}
243-
244-
if currency, ok := param[COIN_ADAPTER_PARAM_CURRENCY]; ok && len(currency) > 0 {
245-
for slot, value := range EncodeBytesToSlots(common.HexToHash(SLOT_COIN_ADAPTER_CURRENCY), []byte(currency)) {
246-
sp = append(sp, params.StateParam{Address: coinAdapterAddress, Key: slot, Value: value})
247-
}
248-
}
249-
250-
// totalSupply: never set during upgrade (runtime path has no alloc)
251-
252-
return sp, nil
253-
}

systemcontracts/gov_base.go

Lines changed: 0 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -335,119 +335,3 @@ func initializeBase(govBaseAddress common.Address, param map[string]string) ([]p
335335
return sp, nil
336336
}
337337

338-
// upgradeBase performs a migration-style upgrade for GovBase storage.
339-
// Only parameters present in param are written to StateParam.
340-
// Missing keys are not appended, so state.SetState is never called for them,
341-
// preserving existing on-chain values automatically.
342-
func upgradeBase(govBaseAddress common.Address, param map[string]string) ([]params.StateParam, error) {
343-
sp := make([]params.StateParam, 0)
344-
345-
if quorumStr, ok := param[GOV_BASE_PARAM_QUORUM]; ok {
346-
quorum, err := strconv.ParseUint(quorumStr, 10, 64)
347-
if err != nil {
348-
return nil, fmt.Errorf("`systemContracts.govBase.params.quorum`: %w", err)
349-
}
350-
if quorum == 0 {
351-
return nil, fmt.Errorf("`systemContracts.govBase.params.quorum` must be greater than 0")
352-
}
353-
sp = append(sp, params.StateParam{
354-
Address: govBaseAddress,
355-
Key: common.HexToHash(SLOT_GOV_BASE_quorum),
356-
Value: common.BigToHash(big.NewInt(int64(quorum))),
357-
})
358-
}
359-
360-
if expiryStr, ok := param[GOV_BASE_PARAM_EXPIRY]; ok {
361-
expiry, err := strconv.ParseUint(expiryStr, 10, 64)
362-
if err != nil {
363-
return nil, fmt.Errorf("`systemContracts.govBase.params.expiry`: %w", err)
364-
}
365-
sp = append(sp, params.StateParam{
366-
Address: govBaseAddress,
367-
Key: common.HexToHash(SLOT_GOV_BASE_proposalExpiry),
368-
Value: common.BigToHash(big.NewInt(int64(expiry))),
369-
})
370-
}
371-
372-
if maxProposalsStr, ok := param[GOV_BASE_PARAM_MAX_PROPOSALS]; ok {
373-
maxProposals, err := strconv.ParseUint(maxProposalsStr, 10, 64)
374-
if err != nil {
375-
return nil, fmt.Errorf("`systemContracts.govBase.params.maxProposals`: %w", err)
376-
}
377-
if maxProposals < 1 || maxProposals > 50 {
378-
return nil, fmt.Errorf("`systemContracts.govBase.params.maxProposals` must be between 1 and 50, got %d", maxProposals)
379-
}
380-
sp = append(sp, params.StateParam{
381-
Address: govBaseAddress,
382-
Key: common.HexToHash(SLOT_GOV_BASE_maxActiveProposalsPerMember),
383-
Value: common.BigToHash(big.NewInt(int64(maxProposals))),
384-
})
385-
}
386-
387-
// members: only process if explicitly provided. Mapping structures are preserved
388-
// automatically when not appended.
389-
if membersStr, ok := param[GOV_BASE_PARAM_MEMBERS]; ok {
390-
memberAddresses := splitAndTrim(membersStr, ",")
391-
uniqueMembers := make([]common.Address, 0)
392-
seen := make(map[common.Address]struct{})
393-
for _, addrStr := range memberAddresses {
394-
member := common.HexToAddress(addrStr)
395-
if member == (common.Address{}) {
396-
return nil, fmt.Errorf("`systemContracts.govBase.params.members` contains invalid zero address")
397-
}
398-
if _, exists := seen[member]; !exists {
399-
seen[member] = struct{}{}
400-
uniqueMembers = append(uniqueMembers, member)
401-
}
402-
}
403-
404-
const MAX_MEMBERS = 255
405-
if len(uniqueMembers) > MAX_MEMBERS {
406-
return nil, fmt.Errorf("`systemContracts.govBase.params.members` count (%d) exceeds maximum allowed (%d)", len(uniqueMembers), MAX_MEMBERS)
407-
}
408-
409-
versionStr, ok2 := param[GOV_BASE_PARAM_MEMBER_VERSION]
410-
if !ok2 {
411-
return nil, fmt.Errorf("`systemContracts.govBase.params.memberVersion` is required when `systemContracts.govBase.params.members` is set")
412-
}
413-
versionInt, err := strconv.ParseUint(versionStr, 10, 64)
414-
if err != nil {
415-
return nil, fmt.Errorf("`systemContracts.govBase.params.memberVersion`: %w", err)
416-
}
417-
version := new(big.Int).SetUint64(versionInt)
418-
419-
membersSlot := common.HexToHash(SLOT_GOV_BASE_members)
420-
versionedMemberListSlot := common.HexToHash(SLOT_GOV_BASE_versionedMemberList)
421-
versionSlot := common.HexToHash(SLOT_GOV_BASE_version)
422-
memberIndexByVersionSlot := common.HexToHash(SLOT_GOV_BASE_memberIndexByVersion)
423-
quorumByVersionSlot := common.HexToHash(SLOT_GOV_BASE_quorumByVersion)
424-
425-
memberData := Member{IsActive: true, JoinedAt: 0}.ToHash()
426-
currentIdx := uint64(0)
427-
for _, member := range uniqueMembers {
428-
if currentIdx >= MAX_MEMBERS {
429-
return nil, fmt.Errorf("member index overflow: currentIdx (%d) >= MAX_MEMBERS (%d)", currentIdx, MAX_MEMBERS)
430-
}
431-
sp = append(sp,
432-
params.StateParam{Address: govBaseAddress, Key: CalculateMappingSlot(membersSlot, member), Value: memberData},
433-
params.StateParam{Address: govBaseAddress, Key: CalculateDynamicSlot(CalculateMappingSlot(versionedMemberListSlot, version), new(big.Int).SetUint64(currentIdx)), Value: common.BytesToHash(member.Bytes())},
434-
params.StateParam{Address: govBaseAddress, Key: CalculateMappingSlot(CalculateMappingSlot(memberIndexByVersionSlot, version), member), Value: common.BigToHash(new(big.Int).SetUint64(currentIdx + 1))},
435-
)
436-
currentIdx++
437-
}
438-
if currentIdx > 0 {
439-
sp = append(sp, params.StateParam{Address: govBaseAddress, Key: CalculateMappingSlot(versionedMemberListSlot, version), Value: common.BigToHash(new(big.Int).SetUint64(currentIdx))})
440-
}
441-
sp = append(sp, params.StateParam{Address: govBaseAddress, Key: versionSlot, Value: common.BigToHash(version)})
442-
443-
// quorumByVersion: only if quorum was also provided
444-
if quorumStr, ok := param[GOV_BASE_PARAM_QUORUM]; ok {
445-
quorum, _ := strconv.ParseUint(quorumStr, 10, 64)
446-
if quorum > 0 {
447-
sp = append(sp, params.StateParam{Address: govBaseAddress, Key: CalculateMappingSlot(quorumByVersionSlot, version), Value: common.BigToHash(new(big.Int).SetUint64(quorum))})
448-
}
449-
}
450-
}
451-
452-
return sp, nil
453-
}

systemcontracts/gov_base_test.go

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ import (
2525

2626
"github.com/ethereum/go-ethereum/common"
2727
"github.com/ethereum/go-ethereum/params"
28-
"github.com/stretchr/testify/assert"
29-
"github.com/stretchr/testify/require"
3028
)
3129

3230
// CompareParam compares two StateParam objects for testing
@@ -575,59 +573,3 @@ func TestValidConfiguration(t *testing.T) {
575573
}
576574
}
577575

578-
// --- upgradeBase tests ---
579-
580-
func TestUpgradeBase_EmptyParams(t *testing.T) {
581-
sp, err := upgradeBase(common.HexToAddress("0x1003"), map[string]string{})
582-
require.NoError(t, err)
583-
assert.Equal(t, 0, len(sp), "Empty params should produce no StateParams")
584-
}
585-
586-
func TestUpgradeBase_AllParams(t *testing.T) {
587-
addr := common.HexToAddress("0x1003")
588-
sp, err := upgradeBase(addr, map[string]string{
589-
"quorum": "4",
590-
"expiry": "86400",
591-
"maxProposals": "10",
592-
})
593-
require.NoError(t, err)
594-
assert.Equal(t, 3, len(sp), "Should have exactly 3 StateParams")
595-
596-
quorumParam := findStateParam(sp, common.HexToHash(SLOT_GOV_BASE_quorum))
597-
require.NotNil(t, quorumParam)
598-
assert.Equal(t, common.BigToHash(big.NewInt(4)), quorumParam.Value)
599-
600-
expiryParam := findStateParam(sp, common.HexToHash(SLOT_GOV_BASE_proposalExpiry))
601-
require.NotNil(t, expiryParam)
602-
assert.Equal(t, common.BigToHash(big.NewInt(86400)), expiryParam.Value)
603-
604-
maxProposalsParam := findStateParam(sp, common.HexToHash(SLOT_GOV_BASE_maxActiveProposalsPerMember))
605-
require.NotNil(t, maxProposalsParam)
606-
assert.Equal(t, common.BigToHash(big.NewInt(10)), maxProposalsParam.Value)
607-
}
608-
609-
func TestUpgradeBase_InvalidQuorum(t *testing.T) {
610-
_, err := upgradeBase(common.HexToAddress("0x1003"), map[string]string{"quorum": "0"})
611-
assert.Error(t, err, "quorum=0 should be rejected")
612-
}
613-
614-
func TestUpgradeBase_InvalidMaxProposals(t *testing.T) {
615-
_, err := upgradeBase(common.HexToAddress("0x1003"), map[string]string{"maxProposals": "51"})
616-
assert.Error(t, err, "maxProposals=51 should be rejected (max 50)")
617-
}
618-
619-
func TestUpgradeBase_MembersWithVersion(t *testing.T) {
620-
sp, err := upgradeBase(common.HexToAddress("0x1003"), map[string]string{
621-
"members": "0xaa5faa65e9cc0f74a85b6fdfb5f6991f5c094697",
622-
"memberVersion": "2",
623-
})
624-
require.NoError(t, err)
625-
assert.True(t, len(sp) > 0, "Should produce member-related StateParams")
626-
}
627-
628-
func TestUpgradeBase_MembersWithoutVersion(t *testing.T) {
629-
_, err := upgradeBase(common.HexToAddress("0x1003"), map[string]string{
630-
"members": "0xaa5faa65e9cc0f74a85b6fdfb5f6991f5c094697",
631-
})
632-
assert.Error(t, err, "members without memberVersion should fail")
633-
}

systemcontracts/gov_council.go

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -108,44 +108,6 @@ func initializeGovCouncil(govCouncilAddress common.Address, param map[string]str
108108
return sp, nil
109109
}
110110

111-
// upgradeGovCouncil performs a migration-style upgrade for GovCouncil.
112-
// Only parameters present in param are written. Missing keys are skipped.
113-
func upgradeGovCouncil(govCouncilAddress common.Address, param map[string]string) ([]params.StateParam, error) {
114-
sp, err := upgradeBase(govCouncilAddress, param)
115-
if err != nil {
116-
return nil, err
117-
}
118-
119-
if blacklistStr, ok := param[GOV_COUNCIL_PARAM_BLACKLIST]; ok && blacklistStr != "" {
120-
blacklistAddresses := splitAndTrim(blacklistStr, ",")
121-
blacklistParams, err := initializeAddressSet(
122-
govCouncilAddress,
123-
common.HexToHash(SLOT_GOV_COUNCIL_currentBlacklist_values),
124-
common.HexToHash(SLOT_GOV_COUNCIL_currentBlacklist_positions),
125-
blacklistAddresses, "blacklist",
126-
)
127-
if err != nil {
128-
return nil, fmt.Errorf("`systemContracts.govCouncil.params.blacklist`: %w", err)
129-
}
130-
sp = append(sp, blacklistParams...)
131-
}
132-
133-
if authorizedAccountsStr, ok := param[GOV_COUNCIL_PARAM_AUTHORIZED_ACCOUNTS]; ok && authorizedAccountsStr != "" {
134-
authorizedAccountAddresses := splitAndTrim(authorizedAccountsStr, ",")
135-
authorizedAccountParams, err := initializeAddressSet(
136-
govCouncilAddress,
137-
common.HexToHash(SLOT_GOV_COUNCIL_currentAuthorizedAccounts_values),
138-
common.HexToHash(SLOT_GOV_COUNCIL_currentAuthorizedAccounts_positions),
139-
authorizedAccountAddresses, "authorizedAccounts",
140-
)
141-
if err != nil {
142-
return nil, fmt.Errorf("`systemContracts.govCouncil.params.authorizedAccounts`: %w", err)
143-
}
144-
sp = append(sp, authorizedAccountParams...)
145-
}
146-
147-
return sp, nil
148-
}
149111

150112
// initializeAddressSet initializes an AddressSet storage structure
151113
// Follows AddressSetLib.sol implementation:

0 commit comments

Comments
 (0)