diff --git a/x/oracle/keeper/migrations.go b/x/oracle/keeper/migrations.go index 69f3f802f..b1ce2720c 100644 --- a/x/oracle/keeper/migrations.go +++ b/x/oracle/keeper/migrations.go @@ -4,6 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" v2 "github.com/shentufoundation/shentu/v2/x/oracle/legacy/v2" + v3 "github.com/shentufoundation/shentu/v2/x/oracle/legacy/v3" ) // Migrator is a struct for handling in-place store migrations. @@ -21,3 +22,8 @@ func (m Migrator) Migrate1to2(ctx sdk.Context) error { v2.UpdateParams(ctx, m.keeper.paramSpace) return v2.MigrateTaskStore(ctx, m.keeper.storeKey, m.keeper.cdc) } + +// Migrate2to3 migrates from version 2 to 3. +func (m Migrator) Migrate2to3(ctx sdk.Context) error { + return v3.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc) +} diff --git a/x/oracle/keeper/task.go b/x/oracle/keeper/task.go index b70039a01..b8080a82a 100644 --- a/x/oracle/keeper/task.go +++ b/x/oracle/keeper/task.go @@ -93,7 +93,7 @@ func (k Keeper) GetTask(ctx sdk.Context, taskID []byte) (task types.TaskI, err e return } -//remove ID of the task from closingBlockStore because it has been handled in shortcut +// DeleteFromClosingTaskIDs remove ID of the task from closingBlockStore because it has been handled in shortcut func (k Keeper) DeleteFromClosingTaskIDs(ctx sdk.Context, task types.TaskI) { taskIDs := k.GetClosingTaskIDs(ctx, task) for i := range taskIDs { diff --git a/x/oracle/legacy/v3/store.go b/x/oracle/legacy/v3/store.go new file mode 100644 index 000000000..cea07ea21 --- /dev/null +++ b/x/oracle/legacy/v3/store.go @@ -0,0 +1,192 @@ +package v3 + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/shentufoundation/shentu/v2/common" + "github.com/shentufoundation/shentu/v2/x/oracle/types" +) + +func MigrateAllTaskStore(ctx sdk.Context, storeKey sdk.StoreKey, cdc codec.BinaryCodec) error { + store := ctx.KVStore(storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.TaskStoreKeyPrefix) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + var oldTask types.TaskI + + err := cdc.UnmarshalInterface(iterator.Value(), &oldTask) + if err != nil { + return err + } + + switch task := oldTask.(type) { + case *types.Task: + if err = MigrateTaskStore(task, store, iterator.Key(), cdc); err != nil { + return err + } + case *types.TxTask: + if err = MigrateTxTaskStore(task, store, iterator.Key(), cdc); err != nil { + return err + } + default: + return fmt.Errorf("err kvstore") + } + + } + return nil +} + +func MigrateTaskStore(task *types.Task, store store.KVStore, key []byte, cdc codec.BinaryCodec) error { + shentuAddr, err := common.PrefixToShentu(task.Creator) + if err != nil { + return err + } + + newTask := types.Task{ + Contract: task.Contract, + Function: task.Function, + BeginBlock: task.BeginBlock, + Bounty: task.Bounty, + Description: task.Description, + Expiration: task.Expiration, + Creator: shentuAddr, + Responses: nil, + Result: task.Result, + ExpireHeight: task.ExpireHeight, + WaitingBlocks: task.WaitingBlocks, + Status: task.Status, + } + + for _, response := range task.Responses { + operator, err := common.PrefixToShentu(response.Operator) + if err != nil { + return err + } + newResponse := types.Response{ + Operator: operator, + Score: response.Score, + Weight: response.Weight, + Reward: response.Reward, + } + newTask.Responses = append(newTask.Responses, newResponse) + } + // delete old task + store.Delete(key) + // set task + bz, err := cdc.MarshalInterface(&newTask) + if err != nil { + return err + } + store.Set(types.TaskStoreKey(newTask.GetID()), bz) + return nil +} + +func MigrateTxTaskStore(task *types.TxTask, store store.KVStore, key []byte, cdc codec.BinaryCodec) error { + shentuAddr, err := common.PrefixToShentu(task.Creator) + if err != nil { + return err + } + + newTask := types.TxTask{ + AtxHash: task.AtxHash, + Creator: shentuAddr, + Bounty: task.Bounty, + ValidTime: task.ValidTime, + Expiration: task.Expiration, + Responses: nil, + Score: task.Score, + Status: task.Status, + } + + for _, response := range task.Responses { + operator, err := common.PrefixToShentu(response.Operator) + if err != nil { + return err + } + newResponse := types.Response{ + Operator: operator, + Score: response.Score, + Weight: response.Weight, + Reward: response.Reward, + } + newTask.Responses = append(newTask.Responses, newResponse) + } + // delete old task + store.Delete(key) + // set task + bz, err := cdc.MarshalInterface(&newTask) + if err != nil { + return err + } + store.Set(types.TaskStoreKey(newTask.GetID()), bz) + return nil +} + +func MigrateOperatorStore(ctx sdk.Context, storeKey sdk.StoreKey, cdc codec.BinaryCodec) error { + store := ctx.KVStore(storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.OperatorStoreKeyPrefix) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + var operator types.Operator + cdc.MustUnmarshalLengthPrefixed(iterator.Value(), &operator) + + shentuOperatorAddress, err := common.PrefixToShentu(operator.Address) + if err != nil { + return err + } + shentuProposal, err := common.PrefixToShentu(operator.Proposer) + if err != nil { + return err + } + + operator.Address = shentuOperatorAddress + operator.Proposer = shentuProposal + + bz := cdc.MustMarshalLengthPrefixed(&operator) + addr := sdk.MustAccAddressFromBech32(shentuOperatorAddress) + store.Set(types.OperatorStoreKey(addr), bz) + } + return nil +} + +func MigrateWithdrawStore(ctx sdk.Context, storeKey sdk.StoreKey, cdc codec.BinaryCodec) error { + store := ctx.KVStore(storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.WithdrawStoreKeyPrefix) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + var withdraw types.Withdraw + cdc.MustUnmarshalLengthPrefixed(iterator.Value(), &withdraw) + + shentuAddr, err := common.PrefixToShentu(withdraw.Address) + if err != nil { + return err + } + + withdraw.Address = shentuAddr + + bz := cdc.MustMarshalLengthPrefixed(&withdraw) + withdrawAddr := sdk.MustAccAddressFromBech32(shentuAddr) + store.Set(types.WithdrawStoreKey(withdrawAddr, withdraw.DueBlock), bz) + } + return nil +} + +func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey, cdc codec.BinaryCodec) error { + if err := MigrateAllTaskStore(ctx, storeKey, cdc); err != nil { + return err + } + if err := MigrateOperatorStore(ctx, storeKey, cdc); err != nil { + return err + } + if err := MigrateWithdrawStore(ctx, storeKey, cdc); err != nil { + return err + } + return nil +} diff --git a/x/oracle/legacy/v3/store_test.go b/x/oracle/legacy/v3/store_test.go new file mode 100644 index 000000000..1bd851bfb --- /dev/null +++ b/x/oracle/legacy/v3/store_test.go @@ -0,0 +1,194 @@ +package v3_test + +import ( + "encoding/hex" + "github.com/stretchr/testify/require" + "math/rand" + "testing" + "time" + + "github.com/tendermint/tendermint/crypto/ed25519" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/bech32" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + + shentuapp "github.com/shentufoundation/shentu/v2/app" + "github.com/shentufoundation/shentu/v2/common" + v3 "github.com/shentufoundation/shentu/v2/x/oracle/legacy/v3" + oracletypes "github.com/shentufoundation/shentu/v2/x/oracle/types" +) + +func Test_MigrateAllTaskStore(t *testing.T) { + app := shentuapp.Setup(false) + cfg := sdk.GetConfig() + cfg.SetBech32PrefixForAccount(common.Bech32PrefixAccAddr, common.Bech32PrefixAccPub) + ctx := app.BaseApp.NewContext(false, tmproto.Header{Time: time.Now().UTC()}) + cdc := shentuapp.MakeEncodingConfig().Marshaler + + store := ctx.KVStore(app.GetKey(oracletypes.StoreKey)) + operator, _ := common.PrefixToCertik(sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address().Bytes()).String()) + // mock old data + + response := oracletypes.Response{ + Operator: operator, + Weight: sdk.NewInt(50), + Reward: nil, + } + + tasks := make(map[string]oracletypes.TaskI) + for i := 0; i < 10; i++ { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + beginBlock := r.Int63n(100) + waitingBlocks := r.Int63n(10) + 1 + expireHeight := beginBlock + waitingBlocks + status := r.Intn(4) + creator, _ := common.PrefixToCertik(sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address().Bytes()).String()) + + task := oracletypes.Task{ + Contract: simtypes.RandStringOfLength(r, 10), + Function: simtypes.RandStringOfLength(r, 5), + BeginBlock: beginBlock, + Bounty: nil, + Description: simtypes.RandStringOfLength(r, 5), + Expiration: time.Time{}, + Creator: creator, + Responses: nil, + Result: simtypes.RandomAmount(r, sdk.NewInt(100)), + ExpireHeight: expireHeight, + WaitingBlocks: waitingBlocks, + Status: oracletypes.TaskStatus(status), + } + task.AddResponse(response) + tasks[string(oracletypes.NewTaskID(task.Contract, task.Function))] = &task + + bz, err := cdc.MarshalInterface(&task) + if err != nil { + panic(err) + } + store.Set(oracletypes.TaskStoreKey(task.GetID()), bz) + + txTask := oracletypes.TxTask{ + AtxHash: []byte(hex.EncodeToString([]byte(simtypes.RandStringOfLength(r, 10)))), + Bounty: nil, + ValidTime: time.Time{}, + Expiration: time.Time{}, + Creator: creator, + Responses: nil, + Status: oracletypes.TaskStatus(status), + Score: r.Int63n(100), + } + txTask.AddResponse(response) + tasks[string(txTask.GetID())] = &txTask + + bz, err = cdc.MarshalInterface(&txTask) + if err != nil { + panic(err) + } + store.Set(oracletypes.TaskStoreKey(txTask.GetID()), bz) + } + + err := v3.MigrateAllTaskStore(ctx, app.GetKey(oracletypes.StoreKey), cdc) + require.Nil(t, err) + + app.OracleKeeper.IteratorAllTasks(ctx, func(task oracletypes.TaskI) bool { + creator := task.GetCreator() + _, _, err := bech32.DecodeAndConvert(creator) + require.NoError(t, err) + + tk, ok := tasks[string(task.GetID())] + require.True(t, ok) + shentuAddr, err := common.PrefixToShentu(tk.GetCreator()) + require.NoError(t, err) + require.Equal(t, shentuAddr, task.GetCreator()) + + require.Equal(t, tk.GetBounty(), task.GetBounty()) + require.Equal(t, tk.GetScore(), task.GetScore()) + return false + }) + +} + +func Test_MigrateOperator(t *testing.T) { + app := shentuapp.Setup(false) + cfg := sdk.GetConfig() + cfg.SetBech32PrefixForAccount(common.Bech32PrefixAccAddr, common.Bech32PrefixAccPub) + ctx := app.BaseApp.NewContext(false, tmproto.Header{Time: time.Now().UTC()}) + cdc := shentuapp.MakeEncodingConfig().Marshaler + + store := ctx.KVStore(app.GetKey(oracletypes.StoreKey)) + // mock old data + for i := 0; i < 10; i++ { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + operatorAddr, _ := common.PrefixToCertik(sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address().Bytes()).String()) + proposerAddr, _ := common.PrefixToCertik(sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address().Bytes()).String()) + + operator := oracletypes.Operator{ + Address: operatorAddr, + Proposer: proposerAddr, + Collateral: nil, + AccumulatedRewards: nil, + Name: simtypes.RandStringOfLength(r, 10), + } + + bz := cdc.MustMarshalLengthPrefixed(&operator) + _, addrBz, err := bech32.DecodeAndConvert(operatorAddr) + if err != nil { + panic(err) + } + addr := sdk.AccAddress(addrBz) + store.Set(oracletypes.OperatorStoreKey(addr), bz) + } + + err := v3.MigrateOperatorStore(ctx, app.GetKey(oracletypes.StoreKey), cdc) + require.Nil(t, err) + + app.OracleKeeper.IterateAllOperators(ctx, func(operator oracletypes.Operator) bool { + hrp, _, err := bech32.DecodeAndConvert(operator.Address) + require.NoError(t, err) + require.Equal(t, hrp, "shentu") + + hrp, _, err = bech32.DecodeAndConvert(operator.Proposer) + require.NoError(t, err) + require.Equal(t, hrp, "shentu") + return false + }) +} + +func Test_MigrateWithdraw(t *testing.T) { + app := shentuapp.Setup(false) + cfg := sdk.GetConfig() + cfg.SetBech32PrefixForAccount(common.Bech32PrefixAccAddr, common.Bech32PrefixAccPub) + ctx := app.BaseApp.NewContext(false, tmproto.Header{Time: time.Now().UTC()}) + cdc := shentuapp.MakeEncodingConfig().Marshaler + + store := ctx.KVStore(app.GetKey(oracletypes.StoreKey)) + // mock old data + for i := 0; i < 10; i++ { + withdrawsAddr, _ := common.PrefixToCertik(sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address().Bytes()).String()) + + withdraw := oracletypes.Withdraw{ + Address: withdrawsAddr, + Amount: nil, + DueBlock: rand.Int63(), + } + + bz := cdc.MustMarshalLengthPrefixed(&withdraw) + withdrawAcc, err := sdk.AccAddressFromBech32(withdrawsAddr) + if err != nil { + panic(err) + } + store.Set(oracletypes.WithdrawStoreKey(withdrawAcc, withdraw.DueBlock), bz) + } + + err := v3.MigrateWithdrawStore(ctx, app.GetKey(oracletypes.StoreKey), cdc) + require.Nil(t, err) + + app.OracleKeeper.IterateAllWithdraws(ctx, func(withdraw oracletypes.Withdraw) bool { + hrp, _, err := bech32.DecodeAndConvert(withdraw.Address) + require.NoError(t, err) + require.Equal(t, hrp, "shentu") + return false + }) +} diff --git a/x/oracle/module.go b/x/oracle/module.go index 86243fe49..c776a7fd3 100644 --- a/x/oracle/module.go +++ b/x/oracle/module.go @@ -112,7 +112,7 @@ func (am AppModule) Name() string { return types.ModuleName } -// RegisterInvariants registers the this module invariants. +// RegisterInvariants registers this module invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { keeper.RegisterInvariants(ir, am.keeper) } @@ -142,6 +142,11 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err != nil { panic(err) } + + err = cfg.RegisterMigration(types.ModuleName, 2, m.Migrate2to3) + if err != nil { + panic(err) + } } // InitGenesis performs genesis initialization for the oracle module. @@ -159,7 +164,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 2 } +func (AppModule) ConsensusVersion() uint64 { return 3 } // BeginBlock implements the Cosmos SDK BeginBlock module function. func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {