diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index d4394e7727..2d5a570285 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -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)) diff --git a/examples/basecoin/x/pow/handler.go b/examples/basecoin/x/pow/handler.go index 1d0b422526..82c8a19c35 100644 --- a/examples/basecoin/x/pow/handler.go +++ b/examples/basecoin/x/pow/handler.go @@ -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{} } diff --git a/examples/basecoin/x/pow/handler_test.go b/examples/basecoin/x/pow/handler_test.go index 8fa8592449..f9db01d0c0 100644 --- a/examples/basecoin/x/pow/handler_test.go +++ b/examples/basecoin/x/pow/handler_test.go @@ -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)) diff --git a/examples/basecoin/x/pow/keeper.go b/examples/basecoin/x/pow/keeper.go new file mode 100644 index 0000000000..dc4494c698 --- /dev/null +++ b/examples/basecoin/x/pow/keeper.go @@ -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 +} diff --git a/examples/basecoin/x/pow/mapper_test.go b/examples/basecoin/x/pow/keeper_test.go similarity index 64% rename from examples/basecoin/x/pow/mapper_test.go rename to examples/basecoin/x/pow/keeper_test.go index 1a190b502a..e9ec9c6d34 100644 --- a/examples/basecoin/x/pow/mapper_test.go +++ b/examples/basecoin/x/pow/keeper_test.go @@ -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)) } diff --git a/examples/basecoin/x/pow/mapper.go b/examples/basecoin/x/pow/mapper.go deleted file mode 100644 index 4dd226bf7b..0000000000 --- a/examples/basecoin/x/pow/mapper.go +++ /dev/null @@ -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))) -}