Merge pull request #792 from cosmos/cwgoes/pow-module
Add PoW module to Democoin
This commit is contained in:
commit
ebe38c91f4
|
@ -19,6 +19,7 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
||||||
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/sketchy"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/sketchy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ type DemocoinApp struct {
|
||||||
// keys to access the substores
|
// keys to access the substores
|
||||||
capKeyMainStore *sdk.KVStoreKey
|
capKeyMainStore *sdk.KVStoreKey
|
||||||
capKeyAccountStore *sdk.KVStoreKey
|
capKeyAccountStore *sdk.KVStoreKey
|
||||||
|
capKeyPowStore *sdk.KVStoreKey
|
||||||
capKeyIBCStore *sdk.KVStoreKey
|
capKeyIBCStore *sdk.KVStoreKey
|
||||||
capKeyStakingStore *sdk.KVStoreKey
|
capKeyStakingStore *sdk.KVStoreKey
|
||||||
|
|
||||||
|
@ -48,6 +50,7 @@ func NewDemocoinApp(logger log.Logger, dbs map[string]dbm.DB) *DemocoinApp {
|
||||||
cdc: MakeCodec(),
|
cdc: MakeCodec(),
|
||||||
capKeyMainStore: sdk.NewKVStoreKey("main"),
|
capKeyMainStore: sdk.NewKVStoreKey("main"),
|
||||||
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
|
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
|
||||||
|
capKeyPowStore: sdk.NewKVStoreKey("pow"),
|
||||||
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
|
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
|
||||||
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
|
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
|
||||||
}
|
}
|
||||||
|
@ -61,20 +64,23 @@ func NewDemocoinApp(logger log.Logger, dbs map[string]dbm.DB) *DemocoinApp {
|
||||||
// add handlers
|
// add handlers
|
||||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
||||||
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
|
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
|
||||||
|
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper)
|
||||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
||||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
||||||
app.Router().
|
app.Router().
|
||||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||||
AddRoute("cool", cool.NewHandler(coolKeeper)).
|
AddRoute("cool", cool.NewHandler(coolKeeper)).
|
||||||
|
AddRoute("pow", powKeeper.Handler).
|
||||||
AddRoute("sketchy", sketchy.NewHandler()).
|
AddRoute("sketchy", sketchy.NewHandler()).
|
||||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
||||||
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
|
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
|
||||||
|
|
||||||
// initialize BaseApp
|
// initialize BaseApp
|
||||||
app.SetTxDecoder(app.txDecoder)
|
app.SetTxDecoder(app.txDecoder)
|
||||||
app.SetInitChainer(app.initChainerFn(coolKeeper))
|
app.SetInitChainer(app.initChainerFn(coolKeeper, powKeeper))
|
||||||
app.MountStoreWithDB(app.capKeyMainStore, sdk.StoreTypeIAVL, dbs["main"])
|
app.MountStoreWithDB(app.capKeyMainStore, sdk.StoreTypeIAVL, dbs["main"])
|
||||||
app.MountStoreWithDB(app.capKeyAccountStore, sdk.StoreTypeIAVL, dbs["acc"])
|
app.MountStoreWithDB(app.capKeyAccountStore, sdk.StoreTypeIAVL, dbs["acc"])
|
||||||
|
app.MountStoreWithDB(app.capKeyPowStore, sdk.StoreTypeIAVL, dbs["pow"])
|
||||||
app.MountStoreWithDB(app.capKeyIBCStore, sdk.StoreTypeIAVL, dbs["ibc"])
|
app.MountStoreWithDB(app.capKeyIBCStore, sdk.StoreTypeIAVL, dbs["ibc"])
|
||||||
app.MountStoreWithDB(app.capKeyStakingStore, sdk.StoreTypeIAVL, dbs["staking"])
|
app.MountStoreWithDB(app.capKeyStakingStore, sdk.StoreTypeIAVL, dbs["staking"])
|
||||||
// NOTE: Broken until #532 lands
|
// NOTE: Broken until #532 lands
|
||||||
|
@ -95,16 +101,18 @@ func MakeCodec() *wire.Codec {
|
||||||
const msgTypeIssue = 0x2
|
const msgTypeIssue = 0x2
|
||||||
const msgTypeQuiz = 0x3
|
const msgTypeQuiz = 0x3
|
||||||
const msgTypeSetTrend = 0x4
|
const msgTypeSetTrend = 0x4
|
||||||
const msgTypeIBCTransferMsg = 0x5
|
const msgTypeMine = 0x5
|
||||||
const msgTypeIBCReceiveMsg = 0x6
|
const msgTypeIBCTransferMsg = 0x6
|
||||||
const msgTypeBondMsg = 0x7
|
const msgTypeIBCReceiveMsg = 0x7
|
||||||
const msgTypeUnbondMsg = 0x8
|
const msgTypeBondMsg = 0x8
|
||||||
|
const msgTypeUnbondMsg = 0x9
|
||||||
var _ = oldwire.RegisterInterface(
|
var _ = oldwire.RegisterInterface(
|
||||||
struct{ sdk.Msg }{},
|
struct{ sdk.Msg }{},
|
||||||
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
|
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
|
||||||
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
|
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
|
||||||
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
|
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
|
||||||
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
|
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
|
||||||
|
oldwire.ConcreteType{pow.MineMsg{}, msgTypeMine},
|
||||||
oldwire.ConcreteType{ibc.IBCTransferMsg{}, msgTypeIBCTransferMsg},
|
oldwire.ConcreteType{ibc.IBCTransferMsg{}, msgTypeIBCTransferMsg},
|
||||||
oldwire.ConcreteType{ibc.IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
|
oldwire.ConcreteType{ibc.IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
|
||||||
oldwire.ConcreteType{simplestake.BondMsg{}, msgTypeBondMsg},
|
oldwire.ConcreteType{simplestake.BondMsg{}, msgTypeBondMsg},
|
||||||
|
@ -143,7 +151,7 @@ func (app *DemocoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom logic for democoin initialization
|
// custom logic for democoin initialization
|
||||||
func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper) sdk.InitChainer {
|
func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keeper) sdk.InitChainer {
|
||||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||||
stateJSON := req.AppStateBytes
|
stateJSON := req.AppStateBytes
|
||||||
|
|
||||||
|
@ -164,7 +172,13 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper) sdk.InitChainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application specific genesis handling
|
// Application specific genesis handling
|
||||||
err = coolKeeper.InitGenesis(ctx, stateJSON)
|
err = coolKeeper.InitGenesis(ctx, genesisState.CoolGenesis)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||||
|
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = powKeeper.InitGenesis(ctx, genesisState.PowGenesis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
||||||
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
@ -71,6 +72,7 @@ func loggerAndDBs() (log.Logger, map[string]dbm.DB) {
|
||||||
dbs := map[string]dbm.DB{
|
dbs := map[string]dbm.DB{
|
||||||
"main": dbm.NewMemDB(),
|
"main": dbm.NewMemDB(),
|
||||||
"acc": dbm.NewMemDB(),
|
"acc": dbm.NewMemDB(),
|
||||||
|
"pow": dbm.NewMemDB(),
|
||||||
"ibc": dbm.NewMemDB(),
|
"ibc": dbm.NewMemDB(),
|
||||||
"staking": dbm.NewMemDB(),
|
"staking": dbm.NewMemDB(),
|
||||||
}
|
}
|
||||||
|
@ -238,6 +240,58 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
||||||
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMineMsg(t *testing.T) {
|
||||||
|
bapp := newDemocoinApp()
|
||||||
|
|
||||||
|
// Construct genesis state
|
||||||
|
// Construct some genesis bytes to reflect democoin/types/AppAccount
|
||||||
|
coins := sdk.Coins{}
|
||||||
|
baseAcc := auth.BaseAccount{
|
||||||
|
Address: addr1,
|
||||||
|
Coins: coins,
|
||||||
|
}
|
||||||
|
acc1 := &types.AppAccount{baseAcc, "foobart"}
|
||||||
|
|
||||||
|
// Construct genesis state
|
||||||
|
genesisState := map[string]interface{}{
|
||||||
|
"accounts": []*types.GenesisAccount{
|
||||||
|
types.NewGenesisAccount(acc1),
|
||||||
|
},
|
||||||
|
"cool": map[string]string{
|
||||||
|
"trend": "ice-cold",
|
||||||
|
},
|
||||||
|
"pow": map[string]uint64{
|
||||||
|
"difficulty": 1,
|
||||||
|
"count": 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// Initialize the chain (nil)
|
||||||
|
vals := []abci.Validator{}
|
||||||
|
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
|
||||||
|
bapp.Commit()
|
||||||
|
|
||||||
|
// A checkTx context (true)
|
||||||
|
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||||
|
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||||
|
assert.Equal(t, acc1, res1)
|
||||||
|
|
||||||
|
// Mine and check for reward
|
||||||
|
mineMsg1 := pow.GenerateMineMsg(addr1, 1, 2)
|
||||||
|
SignCheckDeliver(t, bapp, mineMsg1, 0, true)
|
||||||
|
CheckBalance(t, bapp, "1pow")
|
||||||
|
// Mine again and check for reward
|
||||||
|
mineMsg2 := pow.GenerateMineMsg(addr1, 2, 3)
|
||||||
|
SignCheckDeliver(t, bapp, mineMsg2, 1, true)
|
||||||
|
CheckBalance(t, bapp, "2pow")
|
||||||
|
// Mine again - should be invalid
|
||||||
|
SignCheckDeliver(t, bapp, mineMsg2, 1, false)
|
||||||
|
CheckBalance(t, bapp, "2pow")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestQuizMsg(t *testing.T) {
|
func TestQuizMsg(t *testing.T) {
|
||||||
bapp := newDemocoinApp()
|
bapp := newDemocoinApp()
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,10 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
dbPow, err := dbm.NewGoLevelDB("democoin-pow", filepath.Join(rootDir, "data"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
dbIBC, err := dbm.NewGoLevelDB("democoin-ibc", filepath.Join(rootDir, "data"))
|
dbIBC, err := dbm.NewGoLevelDB("democoin-ibc", filepath.Join(rootDir, "data"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -66,6 +70,7 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||||
dbs := map[string]dbm.DB{
|
dbs := map[string]dbm.DB{
|
||||||
"main": dbMain,
|
"main": dbMain,
|
||||||
"acc": dbAcc,
|
"acc": dbAcc,
|
||||||
|
"pow": dbPow,
|
||||||
"ibc": dbIBC,
|
"ibc": dbIBC,
|
||||||
"staking": dbStaking,
|
"staking": dbStaking,
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
||||||
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ sdk.Account = (*AppAccount)(nil)
|
var _ sdk.Account = (*AppAccount)(nil)
|
||||||
|
@ -41,7 +44,9 @@ func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder {
|
||||||
|
|
||||||
// State to Unmarshal
|
// State to Unmarshal
|
||||||
type GenesisState struct {
|
type GenesisState struct {
|
||||||
Accounts []*GenesisAccount `json:"accounts"`
|
Accounts []*GenesisAccount `json:"accounts"`
|
||||||
|
PowGenesis pow.PowGenesis `json:"pow"`
|
||||||
|
CoolGenesis cool.CoolGenesis `json:"cool"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenesisAccount doesn't need pubkey or sequence
|
// GenesisAccount doesn't need pubkey or sequence
|
||||||
|
|
|
@ -1,17 +1,10 @@
|
||||||
package cool
|
package cool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cool genesis state, containing the genesis trend
|
|
||||||
type GenesisState struct {
|
|
||||||
trend string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keeper - handlers sets/gets of custom variables for your module
|
// Keeper - handlers sets/gets of custom variables for your module
|
||||||
type Keeper struct {
|
type Keeper struct {
|
||||||
ck bank.CoinKeeper
|
ck bank.CoinKeeper
|
||||||
|
@ -49,11 +42,7 @@ func (k Keeper) CheckTrend(ctx sdk.Context, guessedTrend string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitGenesis - store the genesis trend
|
// InitGenesis - store the genesis trend
|
||||||
func (k Keeper) InitGenesis(ctx sdk.Context, data json.RawMessage) error {
|
func (k Keeper) InitGenesis(ctx sdk.Context, data CoolGenesis) error {
|
||||||
var state GenesisState
|
k.setTrend(ctx, data.Trend)
|
||||||
if err := json.Unmarshal(data, &state); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
k.setTrend(ctx, state.trend)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,11 @@ type SetTrendMsg struct {
|
||||||
Cool string
|
Cool string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Genesis state - specify genesis trend
|
||||||
|
type CoolGenesis struct {
|
||||||
|
Trend string `json:"trend"`
|
||||||
|
}
|
||||||
|
|
||||||
// New cool message
|
// New cool message
|
||||||
func NewSetTrendMsg(sender sdk.Address, cool string) SetTrendMsg {
|
func NewSetTrendMsg(sender sdk.Address, cool string) SetTrendMsg {
|
||||||
return SetTrendMsg{
|
return SetTrendMsg{
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||||
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MineCmd(cdc *wire.Codec) *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "mine [difficulty] [count] [nonce] [solution]",
|
||||||
|
Short: "Mine some coins with proof-of-work!",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) != 4 {
|
||||||
|
return errors.New("You must provide a difficulty, a solution, and a nonce (in that order)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get from address and parse arguments
|
||||||
|
|
||||||
|
ctx := context.NewCoreContextFromViper()
|
||||||
|
|
||||||
|
from, err := ctx.GetFromAddress()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
difficulty, err := strconv.ParseUint(args[0], 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := strconv.ParseUint(args[1], 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce, err := strconv.ParseUint(args[2], 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
solution := []byte(args[3])
|
||||||
|
|
||||||
|
msg := pow.NewMineMsg(from, difficulty, count, nonce, solution)
|
||||||
|
|
||||||
|
// get account name
|
||||||
|
name := ctx.FromAddressName
|
||||||
|
|
||||||
|
// build and sign the transaction, then broadcast to Tendermint
|
||||||
|
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package pow
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodeType = sdk.CodeType
|
||||||
|
|
||||||
|
const (
|
||||||
|
CodeInvalidDifficulty CodeType = 201
|
||||||
|
CodeNonexistentDifficulty CodeType = 202
|
||||||
|
CodeNonexistentReward CodeType = 203
|
||||||
|
CodeNonexistentCount CodeType = 204
|
||||||
|
CodeInvalidProof CodeType = 205
|
||||||
|
CodeNotBelowTarget CodeType = 206
|
||||||
|
CodeInvalidCount CodeType = 207
|
||||||
|
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
|
||||||
|
)
|
||||||
|
|
||||||
|
func codeToDefaultMsg(code CodeType) string {
|
||||||
|
switch code {
|
||||||
|
case CodeInvalidDifficulty:
|
||||||
|
return "Insuffient difficulty"
|
||||||
|
case CodeNonexistentDifficulty:
|
||||||
|
return "Nonexistent difficulty"
|
||||||
|
case CodeNonexistentReward:
|
||||||
|
return "Nonexistent reward"
|
||||||
|
case CodeNonexistentCount:
|
||||||
|
return "Nonexistent count"
|
||||||
|
case CodeInvalidProof:
|
||||||
|
return "Invalid proof"
|
||||||
|
case CodeNotBelowTarget:
|
||||||
|
return "Not below target"
|
||||||
|
case CodeInvalidCount:
|
||||||
|
return "Invalid count"
|
||||||
|
case CodeUnknownRequest:
|
||||||
|
return "Unknown request"
|
||||||
|
default:
|
||||||
|
return sdk.CodeToDefaultMsg(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrInvalidDifficulty(msg string) sdk.Error {
|
||||||
|
return newError(CodeInvalidDifficulty, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrNonexistentDifficulty() sdk.Error {
|
||||||
|
return newError(CodeNonexistentDifficulty, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrNonexistentReward() sdk.Error {
|
||||||
|
return newError(CodeNonexistentReward, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrNonexistentCount() sdk.Error {
|
||||||
|
return newError(CodeNonexistentCount, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrInvalidProof(msg string) sdk.Error {
|
||||||
|
return newError(CodeInvalidProof, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrNotBelowTarget(msg string) sdk.Error {
|
||||||
|
return newError(CodeNotBelowTarget, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrInvalidCount(msg string) sdk.Error {
|
||||||
|
return newError(CodeInvalidCount, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func msgOrDefaultMsg(msg string, code CodeType) string {
|
||||||
|
if msg != "" {
|
||||||
|
return msg
|
||||||
|
} else {
|
||||||
|
return codeToDefaultMsg(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newError(code CodeType, msg string) sdk.Error {
|
||||||
|
msg = msgOrDefaultMsg(msg, code)
|
||||||
|
return sdk.NewError(code, msg)
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package pow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
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, pk Keeper, msg MineMsg) sdk.Result {
|
||||||
|
|
||||||
|
// precondition: msg has passed ValidateBasic
|
||||||
|
|
||||||
|
newDiff, newCount, err := pk.CheckValid(ctx, msg.Difficulty, msg.Count)
|
||||||
|
if err != nil {
|
||||||
|
return err.Result()
|
||||||
|
}
|
||||||
|
|
||||||
|
// commented for now, makes testing difficult
|
||||||
|
// TODO figure out a better test method that allows early CheckTx return
|
||||||
|
/*
|
||||||
|
if ctx.IsCheckTx() {
|
||||||
|
return sdk.Result{} // TODO
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
err = pk.ApplyValid(ctx, msg.Sender, newDiff, newCount)
|
||||||
|
if err != nil {
|
||||||
|
return err.Result()
|
||||||
|
}
|
||||||
|
|
||||||
|
return sdk.Result{}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package pow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
auth "github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
bank "github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPowHandler(t *testing.T) {
|
||||||
|
ms, capKey := setupMultiStore()
|
||||||
|
|
||||||
|
am := auth.NewAccountMapper(capKey, &auth.BaseAccount{})
|
||||||
|
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
||||||
|
config := NewPowConfig("pow", int64(1))
|
||||||
|
ck := bank.NewCoinKeeper(am)
|
||||||
|
keeper := NewKeeper(capKey, config, ck)
|
||||||
|
|
||||||
|
handler := keeper.Handler
|
||||||
|
|
||||||
|
addr := sdk.Address([]byte("sender"))
|
||||||
|
count := uint64(1)
|
||||||
|
difficulty := uint64(2)
|
||||||
|
|
||||||
|
err := keeper.InitGenesis(ctx, PowGenesis{uint64(1), uint64(0)})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
nonce, proof := mine(addr, count, difficulty)
|
||||||
|
msg := NewMineMsg(addr, difficulty, count, nonce, proof)
|
||||||
|
|
||||||
|
result := handler(ctx, msg)
|
||||||
|
assert.Equal(t, result, sdk.Result{})
|
||||||
|
|
||||||
|
newDiff, err := keeper.GetLastDifficulty(ctx)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, newDiff, uint64(2))
|
||||||
|
|
||||||
|
newCount, err := keeper.GetLastCount(ctx)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, newCount, uint64(1))
|
||||||
|
|
||||||
|
// todo assert correct coin change, awaiting https://github.com/cosmos/cosmos-sdk/pull/691
|
||||||
|
|
||||||
|
difficulty = uint64(4)
|
||||||
|
nonce, proof = mine(addr, count, difficulty)
|
||||||
|
msg = NewMineMsg(addr, difficulty, count, nonce, proof)
|
||||||
|
|
||||||
|
result = handler(ctx, msg)
|
||||||
|
assert.NotEqual(t, result, sdk.Result{})
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// genesis info must specify starting difficulty and starting count
|
||||||
|
type PowGenesis struct {
|
||||||
|
Difficulty uint64 `json:"difficulty"`
|
||||||
|
Count uint64 `json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Keeper struct {
|
||||||
|
key sdk.StoreKey
|
||||||
|
config PowConfig
|
||||||
|
ck bank.CoinKeeper
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPowConfig(denomination string, reward int64) PowConfig {
|
||||||
|
return PowConfig{denomination, reward}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeeper(key sdk.StoreKey, config PowConfig, ck bank.CoinKeeper) Keeper {
|
||||||
|
return Keeper{key, config, ck}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pk Keeper) InitGenesis(ctx sdk.Context, genesis PowGenesis) error {
|
||||||
|
pk.SetLastDifficulty(ctx, genesis.Difficulty)
|
||||||
|
pk.SetLastCount(ctx, genesis.Count)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
panic("no stored difficulty")
|
||||||
|
} 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 {
|
||||||
|
panic("no stored count")
|
||||||
|
} 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
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package pow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
|
|
||||||
|
"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?
|
||||||
|
func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey) {
|
||||||
|
db := dbm.NewMemDB()
|
||||||
|
capKey := sdk.NewKVStoreKey("capkey")
|
||||||
|
ms := store.NewCommitMultiStore(db)
|
||||||
|
ms.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db)
|
||||||
|
ms.LoadLatestVersion()
|
||||||
|
|
||||||
|
return ms, capKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPowKeeperGetSet(t *testing.T) {
|
||||||
|
ms, capKey := setupMultiStore()
|
||||||
|
|
||||||
|
am := auth.NewAccountMapper(capKey, &auth.BaseAccount{})
|
||||||
|
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
||||||
|
config := NewPowConfig("pow", int64(1))
|
||||||
|
ck := bank.NewCoinKeeper(am)
|
||||||
|
keeper := NewKeeper(capKey, config, ck)
|
||||||
|
|
||||||
|
err := keeper.InitGenesis(ctx, PowGenesis{uint64(1), uint64(0)})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
res, err := keeper.GetLastDifficulty(ctx)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, res, uint64(1))
|
||||||
|
|
||||||
|
keeper.SetLastDifficulty(ctx, 2)
|
||||||
|
|
||||||
|
res, err = keeper.GetLastDifficulty(ctx)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, res, uint64(2))
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package pow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateMineMsg(sender sdk.Address, count uint64, difficulty uint64) MineMsg {
|
||||||
|
nonce, hash := mine(sender, count, difficulty)
|
||||||
|
return NewMineMsg(sender, difficulty, count, nonce, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(sender sdk.Address, count uint64, nonce uint64) []byte {
|
||||||
|
var bytes []byte
|
||||||
|
bytes = append(bytes, []byte(sender)...)
|
||||||
|
countBytes := strconv.FormatUint(count, 16)
|
||||||
|
bytes = append(bytes, countBytes...)
|
||||||
|
nonceBytes := strconv.FormatUint(nonce, 16)
|
||||||
|
bytes = append(bytes, nonceBytes...)
|
||||||
|
hash := crypto.Sha256(bytes)
|
||||||
|
// uint64, so we just use the first 8 bytes of the hash
|
||||||
|
// this limits the range of possible difficulty values (as compared to uint256), but fine for proof-of-concept
|
||||||
|
ret := make([]byte, hex.EncodedLen(len(hash)))
|
||||||
|
hex.Encode(ret, hash)
|
||||||
|
return ret[:16]
|
||||||
|
}
|
||||||
|
|
||||||
|
func mine(sender sdk.Address, count uint64, difficulty uint64) (uint64, []byte) {
|
||||||
|
target := math.MaxUint64 / difficulty
|
||||||
|
for nonce := uint64(0); ; nonce++ {
|
||||||
|
hash := hash(sender, count, nonce)
|
||||||
|
hashuint, err := strconv.ParseUint(string(hash), 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if hashuint < target {
|
||||||
|
return nonce, hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package pow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MineMsg - mine some coins with PoW
|
||||||
|
type MineMsg struct {
|
||||||
|
Sender sdk.Address `json:"sender"`
|
||||||
|
Difficulty uint64 `json:"difficulty"`
|
||||||
|
Count uint64 `json:"count"`
|
||||||
|
Nonce uint64 `json:"nonce"`
|
||||||
|
Proof []byte `json:"proof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// enforce the msg type at compile time
|
||||||
|
var _ sdk.Msg = MineMsg{}
|
||||||
|
|
||||||
|
// NewMineMsg - construct mine message
|
||||||
|
func NewMineMsg(sender sdk.Address, difficulty uint64, count uint64, nonce uint64, proof []byte) MineMsg {
|
||||||
|
return MineMsg{sender, difficulty, count, nonce, proof}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg MineMsg) Type() string { return "pow" }
|
||||||
|
func (msg MineMsg) Get(key interface{}) (value interface{}) { return nil }
|
||||||
|
func (msg MineMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||||
|
func (msg MineMsg) String() string {
|
||||||
|
return fmt.Sprintf("MineMsg{Sender: %v, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg MineMsg) ValidateBasic() sdk.Error {
|
||||||
|
// check hash
|
||||||
|
var data []byte
|
||||||
|
// hash must include sender, so no other users can race the tx
|
||||||
|
data = append(data, []byte(msg.Sender)...)
|
||||||
|
countBytes := strconv.FormatUint(msg.Count, 16)
|
||||||
|
// hash must include count so proof-of-work solutions cannot be replayed
|
||||||
|
data = append(data, countBytes...)
|
||||||
|
nonceBytes := strconv.FormatUint(msg.Nonce, 16)
|
||||||
|
data = append(data, nonceBytes...)
|
||||||
|
hash := crypto.Sha256(data)
|
||||||
|
hashHex := make([]byte, hex.EncodedLen(len(hash)))
|
||||||
|
hex.Encode(hashHex, hash)
|
||||||
|
hashHex = hashHex[:16]
|
||||||
|
if !bytes.Equal(hashHex, msg.Proof) {
|
||||||
|
return ErrInvalidProof(fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
|
||||||
|
}
|
||||||
|
|
||||||
|
// check proof below difficulty
|
||||||
|
// difficulty is linear - 1 = all hashes, 2 = half of hashes, 3 = third of hashes, etc
|
||||||
|
target := math.MaxUint64 / msg.Difficulty
|
||||||
|
hashUint, err := strconv.ParseUint(string(msg.Proof), 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return ErrInvalidProof(fmt.Sprintf("proof: %s", msg.Proof))
|
||||||
|
}
|
||||||
|
if hashUint >= target {
|
||||||
|
return ErrNotBelowTarget(fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg MineMsg) GetSignBytes() []byte {
|
||||||
|
b, err := json.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package pow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewMineMsg(t *testing.T) {
|
||||||
|
addr := sdk.Address([]byte("sender"))
|
||||||
|
msg := MineMsg{addr, 0, 0, 0, []byte("")}
|
||||||
|
equiv := NewMineMsg(addr, 0, 0, 0, []byte(""))
|
||||||
|
assert.Equal(t, msg, equiv, "%s != %s", msg, equiv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMineMsgType(t *testing.T) {
|
||||||
|
addr := sdk.Address([]byte("sender"))
|
||||||
|
msg := MineMsg{addr, 0, 0, 0, []byte("")}
|
||||||
|
assert.Equal(t, msg.Type(), "pow")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMineMsgValidation(t *testing.T) {
|
||||||
|
addr := sdk.Address([]byte("sender"))
|
||||||
|
otherAddr := sdk.Address([]byte("another"))
|
||||||
|
count := uint64(0)
|
||||||
|
for difficulty := uint64(1); difficulty < 1000; difficulty += 100 {
|
||||||
|
count += 1
|
||||||
|
nonce, proof := mine(addr, count, difficulty)
|
||||||
|
msg := MineMsg{addr, difficulty, count, nonce, proof}
|
||||||
|
err := msg.ValidateBasic()
|
||||||
|
assert.Nil(t, err, "error with difficulty %d - %+v", difficulty, err)
|
||||||
|
|
||||||
|
msg.Count += 1
|
||||||
|
err = msg.ValidateBasic()
|
||||||
|
assert.NotNil(t, err, "count was wrong, should have thrown error with msg %s", msg)
|
||||||
|
|
||||||
|
msg.Count -= 1
|
||||||
|
msg.Nonce += 1
|
||||||
|
err = msg.ValidateBasic()
|
||||||
|
assert.NotNil(t, err, "nonce was wrong, should have thrown error with msg %s", msg)
|
||||||
|
|
||||||
|
msg.Nonce -= 1
|
||||||
|
msg.Sender = otherAddr
|
||||||
|
err = msg.ValidateBasic()
|
||||||
|
assert.NotNil(t, err, "sender was wrong, should have thrown error with msg %s", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMineMsgString(t *testing.T) {
|
||||||
|
addr := sdk.Address([]byte("sender"))
|
||||||
|
msg := MineMsg{addr, 0, 0, 0, []byte("abc")}
|
||||||
|
res := msg.String()
|
||||||
|
assert.Equal(t, res, "MineMsg{Sender: 73656E646572, Difficulty: 0, Count: 0, Nonce: 0, Proof: abc}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMineMsgGet(t *testing.T) {
|
||||||
|
addr := sdk.Address([]byte("sender"))
|
||||||
|
msg := MineMsg{addr, 0, 0, 0, []byte("")}
|
||||||
|
res := msg.Get(nil)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMineMsgGetSignBytes(t *testing.T) {
|
||||||
|
addr := sdk.Address([]byte("sender"))
|
||||||
|
msg := MineMsg{addr, 1, 1, 1, []byte("abc")}
|
||||||
|
res := msg.GetSignBytes()
|
||||||
|
assert.Equal(t, string(res), `{"sender":"73656E646572","difficulty":1,"count":1,"nonce":1,"proof":"YWJj"}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMineMsgGetSigners(t *testing.T) {
|
||||||
|
addr := sdk.Address([]byte("sender"))
|
||||||
|
msg := MineMsg{addr, 1, 1, 1, []byte("abc")}
|
||||||
|
res := msg.GetSigners()
|
||||||
|
assert.Equal(t, fmt.Sprintf("%v", res), "[73656E646572]")
|
||||||
|
}
|
Loading…
Reference in New Issue