Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
v102 "github.com/TacBuild/tacchain/app/upgrades/v1.0.2"
v104 "github.com/TacBuild/tacchain/app/upgrades/v1.0.4"
v160 "github.com/TacBuild/tacchain/app/upgrades/v1.6.0"
v160spbhotfix "github.com/TacBuild/tacchain/app/upgrades/v1.6.0-spb-hotfix"
)

// Upgrades list of chain upgrades
Expand All @@ -24,6 +25,7 @@ var Upgrades = []upgrades.Upgrade{
v102.Upgrade, // liquid stake
v104.Upgrade, // ed25519 precompile
v160.Upgrade, // upgrade to cosmos/evm v0.6.0
v160spbhotfix.Upgrade,
}

// RegisterUpgradeHandlers registers the chain upgrade handlers
Expand Down
41 changes: 41 additions & 0 deletions app/upgrades/v1.6.0-spb-hotfix/upgrades.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package v160spbhotfix

import (
"context"
"fmt"

storetypes "cosmossdk.io/store/types"
upgradetypes "cosmossdk.io/x/upgrade/types"

"github.com/TacBuild/tacchain/app/upgrades"
v160 "github.com/TacBuild/tacchain/app/upgrades/v1.6.0"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
)

const UpgradeName = "v1.6.0-spb-hotfix"

var Upgrade = upgrades.Upgrade{
UpgradeName: UpgradeName,
CreateUpgradeHandler: CreateUpgradeHandler,
StoreUpgrades: storetypes.StoreUpgrades{},
}

func CreateUpgradeHandler(
_ upgrades.ModuleManager,
_ module.Configurator,
ak *upgrades.AppKeepers,
) upgradetypes.UpgradeHandler {
return func(ctx context.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
logger := sdkCtx.Logger()

logger.Info("Starting v1.6.0 SPB hotfix upgrade")
if err := v160.MigrateHistoricalGovEVMParamProposals(sdkCtx, ak); err != nil {
return nil, fmt.Errorf("historical gov EVM params proposal migration failed: %w", err)
}

logger.Info("v1.6.0 SPB hotfix upgrade complete")
return fromVM, nil
}
}
173 changes: 173 additions & 0 deletions app/upgrades/v1.6.0/gov_proposal_migration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package v160

import (
"fmt"

storetypes "cosmossdk.io/store/types"
"github.com/cosmos/gogoproto/proto"
gogoany "github.com/cosmos/gogoproto/types/any"
"google.golang.org/protobuf/encoding/protowire"

"github.com/TacBuild/tacchain/app/upgrades"
sdk "github.com/cosmos/cosmos-sdk/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
evmvmtypes "github.com/cosmos/evm/x/vm/types"
)

const evmMsgUpdateParamsTypeURL = "/cosmos.evm.vm.v1.MsgUpdateParams"

// MigrateHistoricalGovEVMParamProposals rewrites old x/vm MsgUpdateParams
// payloads embedded in stored x/gov proposals without unpacking proposal
// interfaces. This repairs historical proposals that were submitted before the
// cosmos/evm Params protobuf field numbers changed.
func MigrateHistoricalGovEVMParamProposals(ctx sdk.Context, ak *upgrades.AppKeepers) error {
storeKey := ak.GetStoreKey(govtypes.StoreKey)
if storeKey == nil {
return fmt.Errorf("gov store key not found")
}
store := ctx.KVStore(storeKey)

var scannedProposals, scannedMessages, rewrittenMessages uint64

iterator := storetypes.KVStorePrefixIterator(store, govtypes.ProposalsKeyPrefix.Bytes())
defer iterator.Close()

for ; iterator.Valid(); iterator.Next() {
scannedProposals++

var proposal govv1.Proposal
if err := proto.Unmarshal(iterator.Value(), &proposal); err != nil {
return fmt.Errorf("unmarshal gov proposal at key %x: %w", iterator.Key(), err)
}

proposalRewritten := false
for i, msg := range proposal.Messages {
scannedMessages++

rewritten, err := migrateHistoricalGovEVMParamProposalMessage(msg)
if err != nil {
return fmt.Errorf("proposal %d message %d: %w", proposal.Id, i, err)
}
if rewritten {
proposalRewritten = true
rewrittenMessages++
}
}

if !proposalRewritten {
continue
}

bz, err := proto.Marshal(&proposal)
if err != nil {
return fmt.Errorf("marshal repaired gov proposal %d: %w", proposal.Id, err)
}
store.Set(iterator.Key(), bz)
}

ctx.Logger().Info(
"Historical gov proposal EVM params migration complete",
"proposals_scanned", scannedProposals,
"messages_scanned", scannedMessages,
"messages_rewritten", rewrittenMessages,
)

return nil
}

func migrateHistoricalGovEVMParamProposalMessage(msg *gogoany.Any) (bool, error) {
if msg == nil || msg.TypeUrl != evmMsgUpdateParamsTypeURL {
return false, nil
}

var current evmvmtypes.MsgUpdateParams
currentErr := proto.Unmarshal(msg.Value, &current)
hasOldWire, wireErr := msgUpdateParamsHasOldEVMParamsWire(msg.Value)
if wireErr != nil {
return false, fmt.Errorf("inspect MsgUpdateParams wire layout: %w", wireErr)
}
if currentErr == nil && !hasOldWire {
return false, nil
}

var old oldEVMMsgUpdateParams
if err := proto.Unmarshal(msg.Value, &old); err != nil {
if currentErr != nil {
return false, fmt.Errorf("unmarshal current MsgUpdateParams: %w; unmarshal old MsgUpdateParams: %v", currentErr, err)
}
return false, fmt.Errorf("unmarshal old MsgUpdateParams: %w", err)
}

newMsg := evmvmtypes.MsgUpdateParams{
Authority: old.Authority,
Params: evmvmtypes.Params{
EvmDenom: old.Params.EvmDenom,
ExtraEIPs: old.Params.ExtraEIPs,
EVMChannels: old.Params.EVMChannels,
AccessControl: old.Params.AccessControl,
ActiveStaticPrecompiles: old.Params.ActiveStaticPrecompiles,
HistoryServeWindow: evmvmtypes.DefaultHistoryServeWindow,
ExtendedDenomOptions: nil,
},
}

bz, err := proto.Marshal(&newMsg)
if err != nil {
return false, fmt.Errorf("marshal current MsgUpdateParams: %w", err)
}
msg.Value = bz

return true, nil
}

func msgUpdateParamsHasOldEVMParamsWire(bz []byte) (bool, error) {
for len(bz) > 0 {
num, typ, tagLen := protowire.ConsumeTag(bz)
if tagLen < 0 {
return false, protowire.ParseError(tagLen)
}
bz = bz[tagLen:]

if num == 2 {
if typ != protowire.BytesType {
return false, fmt.Errorf("unexpected wire type %d for MsgUpdateParams.params", typ)
}
paramsBz, valueLen := protowire.ConsumeBytes(bz)
if valueLen < 0 {
return false, protowire.ParseError(valueLen)
}
return evmParamsHasOldLayoutWire(paramsBz), nil
}

valueLen := protowire.ConsumeFieldValue(num, typ, bz)
if valueLen < 0 {
return false, protowire.ParseError(valueLen)
}
bz = bz[valueLen:]
}

return false, nil
}

func evmParamsHasOldLayoutWire(bz []byte) bool {
for len(bz) > 0 {
num, typ, tagLen := protowire.ConsumeTag(bz)
if tagLen < 0 {
return false
}
bz = bz[tagLen:]

if num == 10 && typ == protowire.BytesType {
return true
}

valueLen := protowire.ConsumeFieldValue(num, typ, bz)
if valueLen < 0 {
return false
}
bz = bz[valueLen:]
}

return false
}
Loading
Loading