Merge old PoW module into Democoin
This commit is contained in:
commit
21fe81720a
|
@ -19,6 +19,7 @@ import (
|
|||
|
||||
"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/pow"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/sketchy"
|
||||
)
|
||||
|
||||
|
@ -34,6 +35,7 @@ type DemocoinApp struct {
|
|||
// keys to access the substores
|
||||
capKeyMainStore *sdk.KVStoreKey
|
||||
capKeyAccountStore *sdk.KVStoreKey
|
||||
capKeyPowStore *sdk.KVStoreKey
|
||||
capKeyIBCStore *sdk.KVStoreKey
|
||||
capKeyStakingStore *sdk.KVStoreKey
|
||||
|
||||
|
@ -48,6 +50,7 @@ func NewDemocoinApp(logger log.Logger, dbs map[string]dbm.DB) *DemocoinApp {
|
|||
cdc: MakeCodec(),
|
||||
capKeyMainStore: sdk.NewKVStoreKey("main"),
|
||||
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
|
||||
capKeyPowStore: sdk.NewKVStoreKey("pow"),
|
||||
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
|
||||
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
|
||||
}
|
||||
|
@ -61,11 +64,13 @@ func NewDemocoinApp(logger log.Logger, dbs map[string]dbm.DB) *DemocoinApp {
|
|||
// add handlers
|
||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
||||
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
|
||||
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper)
|
||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||
AddRoute("cool", cool.NewHandler(coolKeeper)).
|
||||
AddRoute("pow", powKeeper.Handler).
|
||||
AddRoute("sketchy", sketchy.NewHandler()).
|
||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
||||
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
|
||||
|
|
|
@ -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,38 @@
|
|||
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()
|
||||
}
|
||||
|
||||
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,51 @@
|
|||
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)
|
||||
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,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
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
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)
|
||||
|
||||
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,75 @@
|
|||
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"`
|
||||
}
|
||||
|
||||
// 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 "mine" }
|
||||
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,111 @@
|
|||
package pow
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
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(), "mine")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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