refactor(genutil): Use sdk types genesis validator (#21678)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Matt Kocubinski <mkocubinski@gmail.com>
This commit is contained in:
co-authored by
coderabbitai[bot]
Matt Kocubinski
parent
325728a9fd
commit
4aeb053925
@@ -0,0 +1,53 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"cosmossdk.io/errors"
|
||||
|
||||
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
bls12_381 "github.com/cosmos/cosmos-sdk/crypto/keys/bls12_381"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
func PubKeyToProto(pk cryptokeys.JSONPubkey) (cryptotypes.PubKey, error) {
|
||||
switch pk.KeyType {
|
||||
case ed25519.PubKeyName:
|
||||
return &ed25519.PubKey{
|
||||
Key: pk.Value,
|
||||
}, nil
|
||||
case secp256k1.PubKeyName:
|
||||
return &secp256k1.PubKey{
|
||||
Key: pk.Value,
|
||||
}, nil
|
||||
case bls12_381.PubKeyName:
|
||||
return &bls12_381.PubKey{
|
||||
Key: pk.Value,
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.Wrapf(sdkerrors.ErrInvalidType, "cannot convert %v to proto public key", pk)
|
||||
}
|
||||
}
|
||||
|
||||
func PubKeyFromProto(pk cryptotypes.PubKey) (cryptokeys.JSONPubkey, error) {
|
||||
switch pk := pk.(type) {
|
||||
case *ed25519.PubKey:
|
||||
return cryptokeys.JSONPubkey{
|
||||
KeyType: ed25519.PubKeyName,
|
||||
Value: pk.Bytes(),
|
||||
}, nil
|
||||
case *secp256k1.PubKey:
|
||||
return cryptokeys.JSONPubkey{
|
||||
KeyType: secp256k1.PubKeyName,
|
||||
Value: pk.Bytes(),
|
||||
}, nil
|
||||
case *bls12_381.PubKey:
|
||||
return cryptokeys.JSONPubkey{
|
||||
KeyType: bls12_381.PubKeyName,
|
||||
Value: pk.Bytes(),
|
||||
}, nil
|
||||
default:
|
||||
return cryptokeys.JSONPubkey{}, errors.Wrapf(sdkerrors.ErrInvalidType, "cannot convert %v from proto public key", pk)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/bls12_381"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
)
|
||||
|
||||
// JSONPubKey defines a public key that are parse from JSON file.
|
||||
// convert PubKey to JSONPubKey needs a in between step
|
||||
type JSONPubkey struct {
|
||||
KeyType string `json:"type"`
|
||||
Value []byte `json:"value"`
|
||||
}
|
||||
|
||||
func (pk JSONPubkey) Address() types.Address {
|
||||
switch pk.KeyType {
|
||||
case ed25519.PubKeyName:
|
||||
ed25519 := ed25519.PubKey{
|
||||
Key: pk.Value,
|
||||
}
|
||||
return ed25519.Address()
|
||||
case secp256k1.PubKeyName:
|
||||
secp256k1 := secp256k1.PubKey{
|
||||
Key: pk.Value,
|
||||
}
|
||||
return secp256k1.Address()
|
||||
case bls12_381.PubKeyName:
|
||||
bls12_381 := bls12_381.PubKey{
|
||||
Key: pk.Value,
|
||||
}
|
||||
return bls12_381.Address()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (pk JSONPubkey) Bytes() []byte {
|
||||
return pk.Value
|
||||
}
|
||||
@@ -89,11 +89,6 @@ func (a *App[T]) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetStore returns the app store.
|
||||
func (a *App[T]) GetStore() Store {
|
||||
return a.db
|
||||
}
|
||||
|
||||
func (a *App[T]) GetAppManager() *appmanager.AppManager[T] {
|
||||
return a.AppManager
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ func (a *AppBuilder[T]) Build(opts ...AppBuilderOption[T]) (*App[T], error) {
|
||||
return nil, errors.New("cannot init genesis on non-zero state")
|
||||
}
|
||||
genesisCtx := services.NewGenesisContext(a.branch(zeroState))
|
||||
genesisState, err := genesisCtx.Run(ctx, func(ctx context.Context) error {
|
||||
genesisState, err := genesisCtx.Mutate(ctx, func(ctx context.Context) error {
|
||||
err = a.app.moduleManager.InitGenesisJSON(ctx, genesisJSON, txHandler)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to init genesis: %w", err)
|
||||
|
||||
@@ -230,15 +230,17 @@ func (m *MM[T]) ExportGenesisForModules(
|
||||
channels[moduleName] = make(chan genesisResult)
|
||||
go func(moduleI ModuleI, ch chan genesisResult) {
|
||||
genesisCtx := services.NewGenesisContext(stateFactory())
|
||||
_, _ = genesisCtx.Run(ctx, func(ctx context.Context) error {
|
||||
err := genesisCtx.Read(ctx, func(ctx context.Context) error {
|
||||
jm, err := moduleI.ExportGenesis(ctx)
|
||||
if err != nil {
|
||||
ch <- genesisResult{nil, err}
|
||||
return err
|
||||
}
|
||||
ch <- genesisResult{jm, nil}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
ch <- genesisResult{nil, err}
|
||||
}
|
||||
}(moduleI, channels[moduleName])
|
||||
}
|
||||
|
||||
@@ -783,7 +785,9 @@ func messagePassingInterceptor(msg transaction.Msg) grpc.UnaryServerInterceptor
|
||||
}
|
||||
|
||||
// requestFullNameFromMethodDesc returns the fully-qualified name of the request message and response of the provided service's method.
|
||||
func requestFullNameFromMethodDesc(sd *grpc.ServiceDesc, method grpc.MethodDesc) (protoreflect.FullName, protoreflect.FullName, error) {
|
||||
func requestFullNameFromMethodDesc(sd *grpc.ServiceDesc, method grpc.MethodDesc) (
|
||||
protoreflect.FullName, protoreflect.FullName, error,
|
||||
) {
|
||||
methodFullName := protoreflect.FullName(fmt.Sprintf("%s.%s", sd.ServiceName, method.MethodName))
|
||||
desc, err := gogoproto.HybridResolver.FindDescriptorByName(methodFullName)
|
||||
if err != nil {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
var (
|
||||
_ store.KVStoreService = (*GenesisKVStoreService)(nil)
|
||||
_ header.Service = (*GenesisHeaderService)(nil)
|
||||
_ store.KVStore = (*readonlyKVStore)(nil)
|
||||
)
|
||||
|
||||
type genesisContextKeyType struct{}
|
||||
@@ -21,28 +22,41 @@ var genesisContextKey = genesisContextKeyType{}
|
||||
// it backs the store.KVStoreService and header.Service interface implementations
|
||||
// defined in this file.
|
||||
type genesisContext struct {
|
||||
state store.WriterMap
|
||||
state store.ReaderMap
|
||||
}
|
||||
|
||||
// NewGenesisContext creates a new genesis context.
|
||||
func NewGenesisContext(state store.WriterMap) genesisContext {
|
||||
func NewGenesisContext(state store.ReaderMap) genesisContext {
|
||||
return genesisContext{
|
||||
state: state,
|
||||
}
|
||||
}
|
||||
|
||||
// Run runs the provided function within the genesis context and returns an
|
||||
// Mutate runs the provided function within the genesis context and returns an
|
||||
// updated store.WriterMap containing the state modifications made during InitGenesis.
|
||||
func (g *genesisContext) Run(
|
||||
func (g genesisContext) Mutate(
|
||||
ctx context.Context,
|
||||
fn func(ctx context.Context) error,
|
||||
) (store.WriterMap, error) {
|
||||
writerMap, ok := g.state.(store.WriterMap)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("mutate requires a store.WriterMap, got a %T", g.state)
|
||||
}
|
||||
ctx = context.WithValue(ctx, genesisContextKey, g)
|
||||
err := fn(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return g.state, nil
|
||||
return writerMap, nil
|
||||
}
|
||||
|
||||
// Read runs the provided function within the genesis context.
|
||||
func (g genesisContext) Read(
|
||||
ctx context.Context,
|
||||
fn func(ctx context.Context) error,
|
||||
) error {
|
||||
ctx = context.WithValue(ctx, genesisContextKey, g)
|
||||
return fn(ctx)
|
||||
}
|
||||
|
||||
// GenesisKVStoreService is a store.KVStoreService implementation that is used during
|
||||
@@ -71,15 +85,24 @@ func (g *GenesisKVStoreService) OpenKVStore(ctx context.Context) store.KVStore {
|
||||
if v == nil {
|
||||
return g.executionService.OpenKVStore(ctx)
|
||||
}
|
||||
genCtx, ok := v.(*genesisContext)
|
||||
genCtx, ok := v.(genesisContext)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("unexpected genesis context type: %T", v))
|
||||
}
|
||||
state, err := genCtx.state.GetWriter(g.actor)
|
||||
writerMap, ok := genCtx.state.(store.WriterMap)
|
||||
if ok {
|
||||
state, err := writerMap.GetWriter(g.actor)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return state
|
||||
|
||||
}
|
||||
state, err := genCtx.state.GetReader(g.actor)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return state
|
||||
return readonlyKVStore{state}
|
||||
}
|
||||
|
||||
// GenesisHeaderService is a header.Service implementation that is used during
|
||||
@@ -105,3 +128,15 @@ func NewGenesisHeaderService(executionService header.Service) *GenesisHeaderServ
|
||||
executionService: executionService,
|
||||
}
|
||||
}
|
||||
|
||||
type readonlyKVStore struct {
|
||||
store.Reader
|
||||
}
|
||||
|
||||
func (r readonlyKVStore) Set(key, value []byte) error {
|
||||
panic("tried to call Set on a readonly store")
|
||||
}
|
||||
|
||||
func (r readonlyKVStore) Delete(key []byte) error {
|
||||
panic("tried to call Delete on a readonly store")
|
||||
}
|
||||
|
||||
+2
-2
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
|
||||
cmtcrypto "github.com/cometbft/cometbft/crypto"
|
||||
cmttypes "github.com/cometbft/cometbft/types"
|
||||
"github.com/cosmos/gogoproto/grpc"
|
||||
|
||||
"cosmossdk.io/core/server"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/server/api"
|
||||
"github.com/cosmos/cosmos-sdk/server/config"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -76,7 +76,7 @@ type (
|
||||
// AppState is the application state as JSON.
|
||||
AppState json.RawMessage
|
||||
// Validators is the exported validator set.
|
||||
Validators []cmttypes.GenesisValidator
|
||||
Validators []sdk.GenesisValidator
|
||||
// Height is the app's latest block height.
|
||||
Height int64
|
||||
// ConsensusParams are the exported consensus params for ABCI.
|
||||
|
||||
+1
-18
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
|
||||
cmttypes "github.com/cometbft/cometbft/types"
|
||||
|
||||
"cosmossdk.io/collections"
|
||||
storetypes "cosmossdk.io/store/types"
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
"cosmossdk.io/x/staking"
|
||||
stakingtypes "cosmossdk.io/x/staking/types"
|
||||
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
servertypes "github.com/cosmos/cosmos-sdk/server/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
@@ -43,25 +41,10 @@ func (app *SimApp) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAd
|
||||
}
|
||||
|
||||
validators, err := staking.WriteValidators(ctx, app.StakingKeeper)
|
||||
cmtValidators := []cmttypes.GenesisValidator{}
|
||||
for _, val := range validators {
|
||||
cmtPk, err := cryptocodec.ToCmtPubKeyInterface(val.PubKey)
|
||||
if err != nil {
|
||||
return servertypes.ExportedApp{}, err
|
||||
}
|
||||
cmtVal := cmttypes.GenesisValidator{
|
||||
Address: val.Address.Bytes(),
|
||||
PubKey: cmtPk,
|
||||
Power: val.Power,
|
||||
Name: val.Name,
|
||||
}
|
||||
|
||||
cmtValidators = append(cmtValidators, cmtVal)
|
||||
}
|
||||
|
||||
return servertypes.ExportedApp{
|
||||
AppState: appState,
|
||||
Validators: cmtValidators,
|
||||
Validators: validators,
|
||||
Height: height,
|
||||
ConsensusParams: app.BaseApp.GetConsensusParams(ctx),
|
||||
}, err
|
||||
|
||||
+9
-4
@@ -13,10 +13,12 @@ import (
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/runtime/v2"
|
||||
serverstore "cosmossdk.io/server/v2/store"
|
||||
"cosmossdk.io/store/v2"
|
||||
"cosmossdk.io/store/v2/root"
|
||||
basedepinject "cosmossdk.io/x/accounts/defaults/base/depinject"
|
||||
lockupdepinject "cosmossdk.io/x/accounts/defaults/lockup/depinject"
|
||||
multisigdepinject "cosmossdk.io/x/accounts/defaults/multisig/depinject"
|
||||
stakingkeeper "cosmossdk.io/x/staking/keeper"
|
||||
upgradekeeper "cosmossdk.io/x/upgrade/keeper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
@@ -38,10 +40,12 @@ type SimApp[T transaction.Tx] struct {
|
||||
appCodec codec.Codec
|
||||
txConfig client.TxConfig
|
||||
interfaceRegistry codectypes.InterfaceRegistry
|
||||
store store.RootStore
|
||||
|
||||
// required keepers during wiring
|
||||
// others keepers are all in the app
|
||||
UpgradeKeeper *upgradekeeper.Keeper
|
||||
StakingKeeper *stakingkeeper.Keeper
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -161,6 +165,7 @@ func NewSimApp[T transaction.Tx](
|
||||
&app.txConfig,
|
||||
&app.interfaceRegistry,
|
||||
&app.UpgradeKeeper,
|
||||
&app.StakingKeeper,
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -170,7 +175,8 @@ func NewSimApp[T transaction.Tx](
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = storeBuilder.Build(logger, storeConfig)
|
||||
|
||||
app.store, err = storeBuilder.Build(logger, storeConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -213,7 +219,6 @@ func (app *SimApp[T]) TxConfig() client.TxConfig {
|
||||
return app.txConfig
|
||||
}
|
||||
|
||||
// GetStore gets the app store.
|
||||
func (app *SimApp[T]) GetStore() any {
|
||||
return app.App.GetStore()
|
||||
func (app *SimApp[T]) GetStore() store.RootStore {
|
||||
return app.store
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
sdkmath "cosmossdk.io/math"
|
||||
"cosmossdk.io/runtime/v2"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
comettypes "cosmossdk.io/server/v2/cometbft/types"
|
||||
serverv2store "cosmossdk.io/server/v2/store"
|
||||
"cosmossdk.io/store/v2/db"
|
||||
banktypes "cosmossdk.io/x/bank/types"
|
||||
@@ -75,7 +74,7 @@ func NewTestApp(t *testing.T) (*SimApp[transaction.Tx], context.Context) {
|
||||
genesisBytes, err := json.Marshal(genesis)
|
||||
require.NoError(t, err)
|
||||
|
||||
st := app.GetStore().(comettypes.Store)
|
||||
st := app.GetStore()
|
||||
ci, err := st.LastCommitID()
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -111,7 +110,7 @@ func MoveNextBlock(t *testing.T, app *SimApp[transaction.Tx], ctx context.Contex
|
||||
|
||||
bz := sha256.Sum256([]byte{})
|
||||
|
||||
st := app.GetStore().(comettypes.Store)
|
||||
st := app.GetStore()
|
||||
ci, err := st.LastCommitID()
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
+27
-8
@@ -3,27 +3,46 @@ package simapp
|
||||
import (
|
||||
"context"
|
||||
|
||||
"cosmossdk.io/runtime/v2/services"
|
||||
"cosmossdk.io/x/staking"
|
||||
|
||||
v2 "github.com/cosmos/cosmos-sdk/x/genutil/v2"
|
||||
)
|
||||
|
||||
// ExportAppStateAndValidators exports the state of the application for a genesis
|
||||
// file.
|
||||
func (app *SimApp[T]) ExportAppStateAndValidators(jailAllowedAddrs []string) (v2.ExportedApp, error) {
|
||||
// as if they could withdraw from the start of the next block
|
||||
// This is a demonstation of how to export a genesis file. Export may need extended at
|
||||
// the user discretion for cleaning the genesis state at the end provided with jailAllowedAddrs
|
||||
func (app *SimApp[T]) ExportAppStateAndValidators(
|
||||
jailAllowedAddrs []string,
|
||||
) (v2.ExportedApp, error) {
|
||||
ctx := context.Background()
|
||||
var exportedApp v2.ExportedApp
|
||||
|
||||
latestHeight, err := app.LoadLatestHeight()
|
||||
if err != nil {
|
||||
return v2.ExportedApp{}, err
|
||||
return exportedApp, err
|
||||
}
|
||||
|
||||
genesis, err := app.ExportGenesis(ctx, latestHeight)
|
||||
if err != nil {
|
||||
return v2.ExportedApp{}, err
|
||||
return exportedApp, err
|
||||
}
|
||||
|
||||
return v2.ExportedApp{
|
||||
AppState: genesis,
|
||||
Height: int64(latestHeight),
|
||||
}, nil
|
||||
readerMap, err := app.GetStore().StateAt(latestHeight)
|
||||
if err != nil {
|
||||
return exportedApp, err
|
||||
}
|
||||
genesisCtx := services.NewGenesisContext(readerMap)
|
||||
err = genesisCtx.Read(ctx, func(ctx context.Context) error {
|
||||
exportedApp.Validators, err = staking.WriteValidators(ctx, app.StakingKeeper)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return exportedApp, err
|
||||
}
|
||||
|
||||
exportedApp.AppState = genesis
|
||||
exportedApp.Height = int64(latestHeight)
|
||||
return exportedApp, nil
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/server/types"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
gentestutil "github.com/cosmos/cosmos-sdk/testutil/x/genutil"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
|
||||
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
@@ -184,7 +185,7 @@ func setupApp(t *testing.T, tempDir string) (*simapp.SimApp, context.Context, ge
|
||||
ChainID: "theChainId",
|
||||
AppState: stateBytes,
|
||||
Consensus: &genutiltypes.ConsensusGenesis{
|
||||
Validators: nil,
|
||||
Validators: []sdk.GenesisValidator{},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package types
|
||||
|
||||
import cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
|
||||
type GenesisValidator struct {
|
||||
Address ConsAddress
|
||||
PubKey cryptotypes.PubKey
|
||||
Power int64
|
||||
Name string
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package types
|
||||
import (
|
||||
"cosmossdk.io/math"
|
||||
|
||||
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
)
|
||||
|
||||
@@ -98,3 +99,11 @@ type ValidatorI interface {
|
||||
SharesFromTokens(amt math.Int) (math.LegacyDec, error) // shares worth of delegator's bond
|
||||
SharesFromTokensTruncated(amt math.Int) (math.LegacyDec, error) // truncated shares worth of delegator's bond
|
||||
}
|
||||
|
||||
// GenesisValidator is an initial validator.
|
||||
type GenesisValidator struct {
|
||||
Address ConsAddress `json:"address"`
|
||||
PubKey cryptokeys.JSONPubkey `json:"pub_key"`
|
||||
Power int64 `json:"power"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
@@ -13,13 +13,19 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
v052 "github.com/cosmos/cosmos-sdk/x/genutil/migration/v052"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
)
|
||||
|
||||
const flagGenesisTime = "genesis-time"
|
||||
const (
|
||||
flagGenesisTime = "genesis-time"
|
||||
v52 = "v0.52"
|
||||
)
|
||||
|
||||
// MigrationMap is a map of SDK versions to their respective genesis migration functions.
|
||||
var MigrationMap = types.MigrationMap{}
|
||||
var MigrationMap = types.MigrationMap{
|
||||
v52: v052.Migrate,
|
||||
}
|
||||
|
||||
// MigrateGenesisCmd returns a command to execute genesis state migration.
|
||||
// Applications should pass their own migration map to this function.
|
||||
@@ -56,7 +62,17 @@ func MigrateHandler(cmd *cobra.Command, args []string, migrations types.Migratio
|
||||
}
|
||||
|
||||
importGenesis := args[1]
|
||||
appGenesis, err := types.AppGenesisFromFile(importGenesis)
|
||||
outputDocument, _ := cmd.Flags().GetString(flags.FlagOutputDocument)
|
||||
|
||||
// for v52 we need to migrate the consensus validator address from hex bytes to
|
||||
// sdk consensus address.
|
||||
var appGenesis *types.AppGenesis
|
||||
var err error
|
||||
if target == v52 {
|
||||
appGenesis, err = v052.MigrateGenesisFile(importGenesis)
|
||||
} else {
|
||||
appGenesis, err = types.AppGenesisFromFile(importGenesis)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -110,7 +126,6 @@ func MigrateHandler(cmd *cobra.Command, args []string, migrations types.Migratio
|
||||
return fmt.Errorf("failed to marshal app genesis: %w", err)
|
||||
}
|
||||
|
||||
outputDocument, _ := cmd.Flags().GetString(flags.FlagOutputDocument)
|
||||
if outputDocument == "" {
|
||||
cmd.Println(string(bz))
|
||||
return nil
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
cmtjson "github.com/cometbft/cometbft/libs/json"
|
||||
cmttypes "github.com/cometbft/cometbft/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
)
|
||||
|
||||
type legacyAppGenesis struct {
|
||||
AppName string `json:"app_name"`
|
||||
AppVersion string `json:"app_version"`
|
||||
GenesisTime time.Time `json:"genesis_time"`
|
||||
ChainID string `json:"chain_id"`
|
||||
InitialHeight int64 `json:"initial_height"`
|
||||
AppHash []byte `json:"app_hash"`
|
||||
AppState json.RawMessage `json:"app_state,omitempty"`
|
||||
Consensus *legacyConsensusGenesis `json:"consensus,omitempty"`
|
||||
}
|
||||
|
||||
type legacyConsensusGenesis struct {
|
||||
Validators []cmttypes.GenesisValidator `json:"validators,omitempty"`
|
||||
Params *cmttypes.ConsensusParams `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
func MigrateGenesisFile(oldGenFile string) (*types.AppGenesis, error) {
|
||||
file, err := os.Open(filepath.Clean(oldGenFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
appGenesis, err := migrateGenesisValidator(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read genesis from file %s: %w", oldGenFile, err)
|
||||
}
|
||||
|
||||
return appGenesis, nil
|
||||
}
|
||||
|
||||
// migrateGenesisValidator migrate current genesis file genesis validator to match of the
|
||||
// new genesis validator type.
|
||||
func migrateGenesisValidator(r io.Reader) (*types.AppGenesis, error) {
|
||||
var newAg types.AppGenesis
|
||||
var ag legacyAppGenesis
|
||||
var err error
|
||||
|
||||
if rs, ok := r.(io.ReadSeeker); ok {
|
||||
err = json.NewDecoder(rs).Decode(&ag)
|
||||
if err == nil {
|
||||
vals, err := convertValidators(ag.Consensus.Validators)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newAg = types.AppGenesis{
|
||||
AppName: ag.AppName,
|
||||
AppVersion: ag.AppVersion,
|
||||
GenesisTime: ag.GenesisTime,
|
||||
ChainID: ag.ChainID,
|
||||
InitialHeight: ag.InitialHeight,
|
||||
AppHash: ag.AppHash,
|
||||
AppState: ag.AppState,
|
||||
Consensus: &types.ConsensusGenesis{
|
||||
Validators: vals,
|
||||
Params: ag.Consensus.Params,
|
||||
},
|
||||
}
|
||||
|
||||
return &newAg, nil
|
||||
}
|
||||
|
||||
err = fmt.Errorf("error unmarshalling legacy AppGenesis: %w", err)
|
||||
if _, serr := rs.Seek(0, io.SeekStart); serr != nil {
|
||||
err = errors.Join(err, fmt.Errorf("error seeking back to the front: %w", serr))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
jsonBlob, ioerr := io.ReadAll(r)
|
||||
if ioerr != nil {
|
||||
err = errors.Join(err, fmt.Errorf("failed to read file completely: %w", ioerr))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// fallback to comet genesis parsing
|
||||
var ctmGenesis cmttypes.GenesisDoc
|
||||
if uerr := cmtjson.Unmarshal(jsonBlob, &ctmGenesis); uerr != nil {
|
||||
err = errors.Join(err, fmt.Errorf("failed fallback to CometBFT GenDoc: %w", uerr))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vals, err := convertValidators(ctmGenesis.Validators)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newAg = types.AppGenesis{
|
||||
AppName: version.AppName,
|
||||
GenesisTime: ctmGenesis.GenesisTime,
|
||||
ChainID: ctmGenesis.ChainID,
|
||||
InitialHeight: ctmGenesis.InitialHeight,
|
||||
AppHash: ctmGenesis.AppHash,
|
||||
AppState: ctmGenesis.AppState,
|
||||
Consensus: &types.ConsensusGenesis{
|
||||
Validators: vals,
|
||||
Params: ctmGenesis.ConsensusParams,
|
||||
},
|
||||
}
|
||||
|
||||
return &newAg, nil
|
||||
}
|
||||
|
||||
func convertValidators(cmtVals []cmttypes.GenesisValidator) ([]sdk.GenesisValidator, error) {
|
||||
vals := make([]sdk.GenesisValidator, len(cmtVals))
|
||||
for i, cmtVal := range cmtVals {
|
||||
pk, err := cryptocodec.FromCmtPubKeyInterface(cmtVal.PubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonPk, err := cryptocodec.PubKeyFromProto(pk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vals[i] = sdk.GenesisValidator{
|
||||
Address: cmtVal.Address.Bytes(),
|
||||
PubKey: jsonPk,
|
||||
Power: cmtVal.Power,
|
||||
Name: cmtVal.Name,
|
||||
}
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// CometBFT Genesis Handling for JSON,
|
||||
// this is necessary for json unmarshaling of legacyConsensusGenesis
|
||||
func (cs *legacyConsensusGenesis) MarshalJSON() ([]byte, error) {
|
||||
type Alias legacyConsensusGenesis
|
||||
return cmtjson.Marshal(&Alias{
|
||||
Validators: cs.Validators,
|
||||
Params: cs.Params,
|
||||
})
|
||||
}
|
||||
|
||||
func (cs *legacyConsensusGenesis) UnmarshalJSON(b []byte) error {
|
||||
type Alias legacyConsensusGenesis
|
||||
|
||||
result := Alias{}
|
||||
if err := cmtjson.Unmarshal(b, &result); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs.Params = result.Params
|
||||
cs.Validators = result.Validators
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// since we only need migrate the consensus validators content so there is no
|
||||
// exported state migration.
|
||||
func Migrate(appState types.AppMap, _ client.Context) (types.AppMap, error) {
|
||||
return appState, nil
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
)
|
||||
|
||||
var oldGenFilePath = "./testdata/old_app_genesis.json"
|
||||
|
||||
func TestMigration(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
// clean all content on this directory
|
||||
err := os.RemoveAll(tempDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// should not be able to get app genesis from new genesis file
|
||||
// since validators address are still in hex string and not cons address
|
||||
_, err = types.AppGenesisFromFile(oldGenFilePath)
|
||||
require.ErrorContains(t, err, "error unmarshalling AppGenesis: decoding bech32 failed")
|
||||
|
||||
newAppGenesis, err := MigrateGenesisFile(oldGenFilePath)
|
||||
require.NoError(t, err)
|
||||
// save the new app genesis to new temp dir
|
||||
err = newAppGenesis.SaveAs(tempDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
// read the old app genesis to compare with the new app genesis
|
||||
var oldAppGenesis legacyAppGenesis
|
||||
r, err := os.Open(oldGenFilePath)
|
||||
require.NoError(t, err)
|
||||
err = json.NewDecoder(r).Decode(&oldAppGenesis)
|
||||
require.NoError(t, err)
|
||||
|
||||
// should be able to get app genesis from new genesis file
|
||||
newAppGenesis, err = types.AppGenesisFromFile(tempDir)
|
||||
require.NotNil(t, newAppGenesis)
|
||||
require.NotNil(t, newAppGenesis.Consensus)
|
||||
require.True(t, bytes.Equal(oldAppGenesis.AppHash, newAppGenesis.AppHash))
|
||||
require.True(t, bytes.Equal(oldAppGenesis.Consensus.Validators[0].Address.Bytes(), newAppGenesis.Consensus.Validators[0].Address.Bytes()))
|
||||
require.True(t, bytes.Equal(oldAppGenesis.Consensus.Validators[0].PubKey.Bytes(), newAppGenesis.Consensus.Validators[0].PubKey.Bytes()))
|
||||
require.Equal(t, len(oldAppGenesis.Consensus.Validators), len(newAppGenesis.Consensus.Validators), "Number of validators should remain the same after migration")
|
||||
|
||||
require.NoError(t, err)
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -14,6 +14,8 @@ import (
|
||||
cmtjson "github.com/cometbft/cometbft/libs/json"
|
||||
cmttypes "github.com/cometbft/cometbft/types"
|
||||
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
)
|
||||
|
||||
@@ -118,6 +120,26 @@ func AppGenesisFromReader(reader io.Reader) (*AppGenesis, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vals := []sdk.GenesisValidator{}
|
||||
for _, cmtVal := range ctmGenesis.Validators {
|
||||
pk, err := cryptocodec.FromCmtPubKeyInterface(cmtVal.PubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonPk, err := cryptocodec.PubKeyFromProto(pk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val := sdk.GenesisValidator{
|
||||
Address: cmtVal.Address.Bytes(),
|
||||
PubKey: jsonPk,
|
||||
Power: cmtVal.Power,
|
||||
Name: cmtVal.Name,
|
||||
}
|
||||
|
||||
vals = append(vals, val)
|
||||
}
|
||||
|
||||
ag = AppGenesis{
|
||||
AppName: version.AppName,
|
||||
// AppVersion is not filled as we do not know it from a CometBFT genesis
|
||||
@@ -127,7 +149,7 @@ func AppGenesisFromReader(reader io.Reader) (*AppGenesis, error) {
|
||||
AppHash: ctmGenesis.AppHash,
|
||||
AppState: ctmGenesis.AppState,
|
||||
Consensus: &ConsensusGenesis{
|
||||
Validators: ctmGenesis.Validators,
|
||||
Validators: vals,
|
||||
Params: ctmGenesis.ConsensusParams,
|
||||
},
|
||||
}
|
||||
@@ -160,13 +182,36 @@ func AppGenesisFromFile(genFile string) (*AppGenesis, error) {
|
||||
|
||||
// ToGenesisDoc converts the AppGenesis to a CometBFT GenesisDoc.
|
||||
func (ag *AppGenesis) ToGenesisDoc() (*cmttypes.GenesisDoc, error) {
|
||||
cmtValidators := []cmttypes.GenesisValidator{}
|
||||
for _, val := range ag.Consensus.Validators {
|
||||
pk, err := cryptocodec.PubKeyToProto(val.PubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmtPk, err := cryptocodec.ToCmtPubKeyInterface(pk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmtVal := cmttypes.GenesisValidator{
|
||||
Address: val.Address.Bytes(),
|
||||
PubKey: cmtPk,
|
||||
Power: val.Power,
|
||||
Name: val.Name,
|
||||
}
|
||||
|
||||
cmtValidators = append(cmtValidators, cmtVal)
|
||||
}
|
||||
// assert nil value for empty validators set
|
||||
if len(cmtValidators) == 0 {
|
||||
cmtValidators = nil
|
||||
}
|
||||
return &cmttypes.GenesisDoc{
|
||||
GenesisTime: ag.GenesisTime,
|
||||
ChainID: ag.ChainID,
|
||||
InitialHeight: ag.InitialHeight,
|
||||
AppHash: ag.AppHash,
|
||||
AppState: ag.AppState,
|
||||
Validators: ag.Consensus.Validators,
|
||||
Validators: cmtValidators,
|
||||
ConsensusParams: ag.Consensus.Params,
|
||||
}, nil
|
||||
}
|
||||
@@ -174,13 +219,13 @@ func (ag *AppGenesis) ToGenesisDoc() (*cmttypes.GenesisDoc, error) {
|
||||
// ConsensusGenesis defines the consensus layer's genesis.
|
||||
// TODO(@julienrbrt) eventually abstract from CometBFT types
|
||||
type ConsensusGenesis struct {
|
||||
Validators []cmttypes.GenesisValidator `json:"validators,omitempty"`
|
||||
Params *cmttypes.ConsensusParams `json:"params,omitempty"`
|
||||
Validators []sdk.GenesisValidator `json:"validators,omitempty"`
|
||||
Params *cmttypes.ConsensusParams `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
// NewConsensusGenesis returns a ConsensusGenesis with given values.
|
||||
// It takes a proto consensus params so it can called from server export command.
|
||||
func NewConsensusGenesis(params cmtproto.ConsensusParams, validators []cmttypes.GenesisValidator) *ConsensusGenesis {
|
||||
func NewConsensusGenesis(params cmtproto.ConsensusParams, validators []sdk.GenesisValidator) *ConsensusGenesis {
|
||||
return &ConsensusGenesis{
|
||||
Params: &cmttypes.ConsensusParams{
|
||||
Block: cmttypes.BlockParams{
|
||||
@@ -211,7 +256,7 @@ func (cs *ConsensusGenesis) MarshalJSON() ([]byte, error) {
|
||||
func (cs *ConsensusGenesis) UnmarshalJSON(b []byte) error {
|
||||
type Alias ConsensusGenesis
|
||||
|
||||
result := Alias{}
|
||||
var result Alias
|
||||
if err := cmtjson.Unmarshal(b, &result); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -241,7 +286,7 @@ func (cs *ConsensusGenesis) ValidateAndComplete() error {
|
||||
return fmt.Errorf("incorrect address for validator %v in the genesis file, should be %v", v, v.PubKey.Address())
|
||||
}
|
||||
if len(v.Address) == 0 {
|
||||
cs.Validators[i].Address = v.PubKey.Address()
|
||||
cs.Validators[i].Address = v.PubKey.Address().Bytes()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
@@ -14,11 +14,11 @@ import (
|
||||
tmed25519 "github.com/cometbft/cometbft/crypto/ed25519"
|
||||
"github.com/cometbft/cometbft/p2p"
|
||||
"github.com/cometbft/cometbft/privval"
|
||||
cmttypes "github.com/cometbft/cometbft/types"
|
||||
"github.com/cosmos/go-bip39"
|
||||
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
)
|
||||
|
||||
@@ -35,7 +35,7 @@ func ExportGenesisFile(genesis *types.AppGenesis, genFile string) error {
|
||||
// ExportGenesisFileWithTime creates and writes the genesis configuration to disk.
|
||||
// An error is returned if building or writing the configuration to file fails.
|
||||
func ExportGenesisFileWithTime(
|
||||
genFile, chainID string, validators []cmttypes.GenesisValidator, appState json.RawMessage, genTime time.Time,
|
||||
genFile, chainID string, validators []sdk.GenesisValidator, appState json.RawMessage, genTime time.Time,
|
||||
) error {
|
||||
appGenesis := types.NewAppGenesisWithVersion(chainID, appState)
|
||||
appGenesis.GenesisTime = genTime
|
||||
|
||||
@@ -76,6 +76,7 @@ func ExportCmd(appExporter v2.AppExporter) *cobra.Command {
|
||||
|
||||
appGenesis.AppState = exported.AppState
|
||||
appGenesis.InitialHeight = exported.Height
|
||||
appGenesis.Consensus.Validators = exported.Validators
|
||||
|
||||
out, err := json.Marshal(appGenesis)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,6 +3,8 @@ package v2
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// AppExporter is a function that dumps all app state to
|
||||
@@ -20,4 +22,6 @@ type ExportedApp struct {
|
||||
AppState json.RawMessage
|
||||
// Height is the app's latest block height.
|
||||
Height int64
|
||||
// Validators is the exported validator set.
|
||||
Validators []sdk.GenesisValidator
|
||||
}
|
||||
|
||||
+9
-16
@@ -9,23 +9,12 @@ import (
|
||||
"cosmossdk.io/x/staking/keeper"
|
||||
"cosmossdk.io/x/staking/types"
|
||||
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// TODO: move this to sdk types and use this instead of comet types GenesisValidator
|
||||
// then we can do pubkey conversion in ToGenesisDoc
|
||||
//
|
||||
// this is a temporary work around to avoid import comet directly in staking
|
||||
type GenesisValidator struct {
|
||||
Address sdk.ConsAddress
|
||||
PubKey cryptotypes.PubKey
|
||||
Power int64
|
||||
Name string
|
||||
}
|
||||
|
||||
// WriteValidators returns a slice of bonded genesis validators.
|
||||
func WriteValidators(ctx context.Context, keeper *keeper.Keeper) (vals []GenesisValidator, returnErr error) {
|
||||
func WriteValidators(ctx context.Context, keeper *keeper.Keeper) (vals []sdk.GenesisValidator, returnErr error) {
|
||||
err := keeper.LastValidatorPower.Walk(ctx, nil, func(key []byte, _ gogotypes.Int64Value) (bool, error) {
|
||||
validator, err := keeper.GetValidator(ctx, key)
|
||||
if err != nil {
|
||||
@@ -37,10 +26,14 @@ func WriteValidators(ctx context.Context, keeper *keeper.Keeper) (vals []Genesis
|
||||
returnErr = err
|
||||
return true, err
|
||||
}
|
||||
jsonPk, err := cryptocodec.PubKeyFromProto(pk)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
vals = append(vals, GenesisValidator{
|
||||
Address: sdk.ConsAddress(pk.Address()),
|
||||
PubKey: pk,
|
||||
vals = append(vals, sdk.GenesisValidator{
|
||||
Address: pk.Address().Bytes(),
|
||||
PubKey: jsonPk,
|
||||
Power: validator.GetConsensusPower(keeper.PowerReduction(ctx)),
|
||||
Name: validator.GetMoniker(),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user