Refactor to keeper-based module (#689)
This commit is contained in:
parent
1b4a3d24ff
commit
47b8767208
@ -60,13 +60,13 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
||||
// add handlers
|
||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
||||
coolMapper := cool.NewMapper(app.capKeyMainStore)
|
||||
powMapper := pow.NewMapper(app.capKeyMainStore)
|
||||
powKeeper := pow.NewKeeper(app.capKeyMainStore, pow.NewPowConfig("pow", int64(1)), coinKeeper)
|
||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
||||
stakingMapper := staking.NewMapper(app.capKeyStakingStore)
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||
AddRoute("cool", cool.NewHandler(coinKeeper, coolMapper)).
|
||||
AddRoute("pow", pow.NewHandler(coinKeeper, powMapper, pow.NewPowConfig("pow", int64(1)))).
|
||||
AddRoute("pow", powKeeper.Handler).
|
||||
AddRoute("sketchy", sketchy.NewHandler()).
|
||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
||||
AddRoute("staking", staking.NewHandler(stakingMapper, coinKeeper))
|
||||
|
||||
@ -1,70 +1,38 @@
|
||||
package pow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
// module users must specify coin denomination and reward (constant) per PoW solution
|
||||
type PowConfig struct {
|
||||
Denomination string
|
||||
Reward int64
|
||||
}
|
||||
|
||||
func NewPowConfig(denomination string, reward int64) PowConfig {
|
||||
return PowConfig{denomination, reward}
|
||||
}
|
||||
|
||||
func NewHandler(ck bank.CoinKeeper, pm Mapper, config PowConfig) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case MineMsg:
|
||||
return handleMineMsg(ctx, ck, pm, config, msg)
|
||||
default:
|
||||
errMsg := "Unrecognized pow Msg type: " + reflect.TypeOf(msg).Name()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
}
|
||||
func (pk Keeper) Handler(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case MineMsg:
|
||||
return handleMineMsg(ctx, pk, msg)
|
||||
default:
|
||||
errMsg := "Unrecognized pow Msg type: " + reflect.TypeOf(msg).Name()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
}
|
||||
}
|
||||
|
||||
func handleMineMsg(ctx sdk.Context, ck bank.CoinKeeper, pm Mapper, config PowConfig, msg MineMsg) sdk.Result {
|
||||
func handleMineMsg(ctx sdk.Context, pk Keeper, msg MineMsg) sdk.Result {
|
||||
|
||||
// precondition: msg has passed ValidateBasic
|
||||
|
||||
// will this function always be applied atomically?
|
||||
|
||||
lastDifficulty, err := pm.GetLastDifficulty(ctx)
|
||||
newDiff, newCount, err := pk.CheckValid(ctx, msg.Difficulty, msg.Count)
|
||||
if err != nil {
|
||||
return ErrNonexistentDifficulty().Result()
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
newDifficulty := lastDifficulty + 1
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{} // TODO
|
||||
}
|
||||
|
||||
lastCount, err := pm.GetLastCount(ctx)
|
||||
err = pk.ApplyValid(ctx, msg.Sender, newDiff, newCount)
|
||||
if err != nil {
|
||||
return ErrNonexistentCount().Result()
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
newCount := lastCount + 1
|
||||
|
||||
if msg.Count != newCount {
|
||||
return ErrInvalidCount(fmt.Sprintf("invalid count: was %d, should have been %d", msg.Count, newCount)).Result()
|
||||
}
|
||||
|
||||
if msg.Difficulty != newDifficulty {
|
||||
return ErrInvalidDifficulty(fmt.Sprintf("invalid difficulty: was %d, should have been %d", msg.Difficulty, newDifficulty)).Result()
|
||||
}
|
||||
|
||||
_, ckErr := ck.AddCoins(ctx, msg.Sender, []sdk.Coin{sdk.Coin{config.Denomination, config.Reward}})
|
||||
if ckErr != nil {
|
||||
return ckErr.Result()
|
||||
}
|
||||
|
||||
pm.SetLastDifficulty(ctx, newDifficulty)
|
||||
pm.SetLastCount(ctx, newCount)
|
||||
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
@ -17,11 +17,11 @@ func TestPowHandler(t *testing.T) {
|
||||
|
||||
am := auth.NewAccountMapper(capKey, &auth.BaseAccount{})
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
||||
mapper := NewMapper(capKey)
|
||||
config := NewPowConfig("pow", int64(1))
|
||||
ck := bank.NewCoinKeeper(am)
|
||||
keeper := NewKeeper(capKey, config, ck)
|
||||
|
||||
handler := NewHandler(ck, mapper, config)
|
||||
handler := keeper.Handler
|
||||
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
count := uint64(1)
|
||||
@ -32,11 +32,11 @@ func TestPowHandler(t *testing.T) {
|
||||
result := handler(ctx, msg)
|
||||
assert.Equal(t, result, sdk.Result{})
|
||||
|
||||
newDiff, err := mapper.GetLastDifficulty(ctx)
|
||||
newDiff, err := keeper.GetLastDifficulty(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, newDiff, uint64(2))
|
||||
|
||||
newCount, err := mapper.GetLastCount(ctx)
|
||||
newCount, err := keeper.GetLastCount(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, newCount, uint64(1))
|
||||
|
||||
|
||||
103
examples/basecoin/x/pow/keeper.go
Normal file
103
examples/basecoin/x/pow/keeper.go
Normal file
@ -0,0 +1,103 @@
|
||||
package pow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
bank "github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
// module users must specify coin denomination and reward (constant) per PoW solution
|
||||
type PowConfig struct {
|
||||
Denomination string
|
||||
Reward int64
|
||||
}
|
||||
|
||||
func NewPowConfig(denomination string, reward int64) PowConfig {
|
||||
return PowConfig{denomination, reward}
|
||||
}
|
||||
|
||||
type Keeper struct {
|
||||
key sdk.StoreKey
|
||||
config PowConfig
|
||||
ck bank.CoinKeeper
|
||||
}
|
||||
|
||||
func NewKeeper(key sdk.StoreKey, config PowConfig, ck bank.CoinKeeper) Keeper {
|
||||
return Keeper{key, config, ck}
|
||||
}
|
||||
|
||||
var lastDifficultyKey = []byte("lastDifficultyKey")
|
||||
|
||||
func (pk Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
stored := store.Get(lastDifficultyKey)
|
||||
if stored == nil {
|
||||
// return the default difficulty of 1 if not set
|
||||
// this works OK for this module, but a way to initalize the store (a "genesis block" for the module) might be better in general
|
||||
return uint64(1), nil
|
||||
} else {
|
||||
return strconv.ParseUint(string(stored), 0, 64)
|
||||
}
|
||||
}
|
||||
|
||||
func (pk Keeper) SetLastDifficulty(ctx sdk.Context, diff uint64) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
store.Set(lastDifficultyKey, []byte(strconv.FormatUint(diff, 16)))
|
||||
}
|
||||
|
||||
var countKey = []byte("count")
|
||||
|
||||
func (pk Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
stored := store.Get(countKey)
|
||||
if stored == nil {
|
||||
return uint64(0), nil
|
||||
} else {
|
||||
return strconv.ParseUint(string(stored), 0, 64)
|
||||
}
|
||||
}
|
||||
|
||||
func (pk Keeper) SetLastCount(ctx sdk.Context, count uint64) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
store.Set(countKey, []byte(strconv.FormatUint(count, 16)))
|
||||
}
|
||||
|
||||
func (pk Keeper) CheckValid(ctx sdk.Context, difficulty uint64, count uint64) (uint64, uint64, sdk.Error) {
|
||||
|
||||
lastDifficulty, err := pk.GetLastDifficulty(ctx)
|
||||
if err != nil {
|
||||
return 0, 0, ErrNonexistentDifficulty()
|
||||
}
|
||||
|
||||
newDifficulty := lastDifficulty + 1
|
||||
|
||||
lastCount, err := pk.GetLastCount(ctx)
|
||||
if err != nil {
|
||||
return 0, 0, ErrNonexistentCount()
|
||||
}
|
||||
|
||||
newCount := lastCount + 1
|
||||
|
||||
if count != newCount {
|
||||
return 0, 0, ErrInvalidCount(fmt.Sprintf("invalid count: was %d, should have been %d", count, newCount))
|
||||
}
|
||||
|
||||
if difficulty != newDifficulty {
|
||||
return 0, 0, ErrInvalidDifficulty(fmt.Sprintf("invalid difficulty: was %d, should have been %d", difficulty, newDifficulty))
|
||||
}
|
||||
|
||||
return newDifficulty, newCount, nil
|
||||
|
||||
}
|
||||
|
||||
func (pk Keeper) ApplyValid(ctx sdk.Context, sender sdk.Address, newDifficulty uint64, newCount uint64) sdk.Error {
|
||||
_, ckErr := pk.ck.AddCoins(ctx, sender, []sdk.Coin{sdk.Coin{pk.config.Denomination, pk.config.Reward}})
|
||||
if ckErr != nil {
|
||||
return ckErr
|
||||
}
|
||||
pk.SetLastDifficulty(ctx, newDifficulty)
|
||||
pk.SetLastCount(ctx, newCount)
|
||||
return nil
|
||||
}
|
||||
@ -10,6 +10,8 @@ import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth"
|
||||
bank "github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
// possibly share this kind of setup functionality between module testsuites?
|
||||
@ -23,19 +25,22 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey) {
|
||||
return ms, capKey
|
||||
}
|
||||
|
||||
func TestPowMapperGetSet(t *testing.T) {
|
||||
func TestPowKeeperGetSet(t *testing.T) {
|
||||
ms, capKey := setupMultiStore()
|
||||
|
||||
am := auth.NewAccountMapper(capKey, &auth.BaseAccount{})
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
||||
mapper := NewMapper(capKey)
|
||||
config := NewPowConfig("pow", int64(1))
|
||||
ck := bank.NewCoinKeeper(am)
|
||||
keeper := NewKeeper(capKey, config, ck)
|
||||
|
||||
res, err := mapper.GetLastDifficulty(ctx)
|
||||
res, err := keeper.GetLastDifficulty(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, res, uint64(1))
|
||||
|
||||
mapper.SetLastDifficulty(ctx, 2)
|
||||
keeper.SetLastDifficulty(ctx, 2)
|
||||
|
||||
res, err = mapper.GetLastDifficulty(ctx)
|
||||
res, err = keeper.GetLastDifficulty(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, res, uint64(2))
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
package pow
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
type Mapper struct {
|
||||
key sdk.StoreKey
|
||||
}
|
||||
|
||||
func NewMapper(key sdk.StoreKey) Mapper {
|
||||
return Mapper{key}
|
||||
}
|
||||
|
||||
var lastDifficultyKey = []byte("lastDifficultyKey")
|
||||
|
||||
func (pm Mapper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(pm.key)
|
||||
stored := store.Get(lastDifficultyKey)
|
||||
if stored == nil {
|
||||
// return the default difficulty of 1 if not set
|
||||
// this works OK for this module, but a way to initalize the store (a "genesis block" for the module) might be better in general
|
||||
return uint64(1), nil
|
||||
} else {
|
||||
return strconv.ParseUint(string(stored), 0, 64)
|
||||
}
|
||||
}
|
||||
|
||||
func (pm Mapper) SetLastDifficulty(ctx sdk.Context, diff uint64) {
|
||||
store := ctx.KVStore(pm.key)
|
||||
store.Set(lastDifficultyKey, []byte(strconv.FormatUint(diff, 16)))
|
||||
}
|
||||
|
||||
var countKey = []byte("count")
|
||||
|
||||
func (pm Mapper) GetLastCount(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(pm.key)
|
||||
stored := store.Get(countKey)
|
||||
if stored == nil {
|
||||
return uint64(0), nil
|
||||
} else {
|
||||
return strconv.ParseUint(string(stored), 0, 64)
|
||||
}
|
||||
}
|
||||
|
||||
func (pm Mapper) SetLastCount(ctx sdk.Context, count uint64) {
|
||||
store := ctx.KVStore(pm.key)
|
||||
store.Set(countKey, []byte(strconv.FormatUint(count, 16)))
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user