Merge branch 'rigel/tick-tests' into joon/732-stake-keeper

This commit is contained in:
Rigel 2018-04-03 00:21:30 +02:00 committed by GitHub
commit 47aaae835e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 2398 additions and 731 deletions

18
.gitignore vendored
View File

@ -1,23 +1,23 @@
# OS
.DS_Store
*.swp
*.swo
.vscode
# Build
vendor
merkleeyes.db
build
docs/guide/*.sh
tools/bin/*
examples/build/*
examples/basecoin/glide.lock
docs/_build
# Data - ideally these don't exist
examples/basecoin/app/data
baseapp/data/*
docs/_build
.DS_Store
# Testing
coverage.txt
profile.out
.vscode
coverage.txt
profile.out
client/lcd/keys.db/
### Vagrant ###
.vagrant/

View File

@ -1,5 +1,37 @@
# Changelog
## 0.13.0 (April 2, 2018)
BREAKING CHANGES
* [basecoin] Remove cool/sketchy modules -> moved to new `democoin`
* [basecoin] NewBasecoinApp takes a `map[string]dbm.DB` as temporary measure
to allow mounting multiple stores with their own DB until they can share one
* [staking] Renamed to `simplestake`
* [builder] Functions don't take `passphrase` as argument
* [server] GenAppState returns generated seed and address
* [basecoind] `init` command outputs JSON of everything necessary for testnet
* [basecoind] `basecoin.db -> data/basecoin.db`
* [basecli] `data/keys.db -> keys/keys.db`
FEATURES
* [types] `Coin` supports direct arithmetic operations
* [basecoind] Add `show_validator` and `show_node_id` commands
* [staking] Initial merge of full staking module!
* [democoin] New example application to demo custom modules
IMPROVEMENTS
* [makefile] `make install`
* [testing] Use `/tmp` for directories so they don't get left in the repo
BUG FIXES
* [basecoin] Allow app to be restarted
* [makefile] Fix build on Windows
* [basecli] Get confirmation before overriding key with same name
## 0.12.0 (March 27 2018)
BREAKING CHANGES

View File

@ -19,14 +19,25 @@ gaia:
build:
@rm -rf $(shell pwd)/examples/basecoin/vendor/
@rm -rf $(shell pwd)/examples/democoin/vendor/
ifeq ($(OS),Windows_NT)
go build $(BUILD_FLAGS) -o build/basecoind.exe ./examples/basecoin/cmd/basecoind
go build $(BUILD_FLAGS) -o build/basecli.exe ./examples/basecoin/cmd/basecli
go build $(BUILD_FLAGS) -o build/democoind.exe ./examples/democoin/cmd/democoind
go build $(BUILD_FLAGS) -o build/democli.exe ./examples/democoin/cmd/democli
else
go build $(BUILD_FLAGS) -o build/basecoind ./examples/basecoin/cmd/basecoind
go build $(BUILD_FLAGS) -o build/basecli ./examples/basecoin/cmd/basecli
go build $(BUILD_FLAGS) -o build/democoind ./examples/democoin/cmd/democoind
go build $(BUILD_FLAGS) -o build/democli ./examples/democoin/cmd/democli
endif
install:
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecoind
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecli
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democoind
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democli
dist:
@bash publish/dist.sh
@bash publish/publish.sh
@ -74,13 +85,12 @@ test: test_unit # test_cli
test_unit:
@rm -rf examples/basecoin/vendor/
@rm -rf examples/democoin/vendor/
@go test $(PACKAGES)
test_cover:
@rm -rf examples/basecoin/vendor/
@rm -rf client/lcd/keys.db ~/.tendermint_test
@bash tests/test_cover.sh
@rm -rf client/lcd/keys.db ~/.tendermint_test
benchmark:
@go test -bench=. $(PACKAGES)

View File

@ -55,6 +55,7 @@ type BaseApp struct {
var _ abci.Application = (*BaseApp)(nil)
// Create and name new BaseApp
// NOTE: The db is used to store the version number for now.
func NewBaseApp(name string, logger log.Logger, db dbm.DB) *BaseApp {
return &BaseApp{
Logger: logger,
@ -71,12 +72,18 @@ func (app *BaseApp) Name() string {
}
// Mount a store to the provided key in the BaseApp multistore
// Broken until #532 is implemented.
func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) {
for _, key := range keys {
app.MountStore(key, sdk.StoreTypeIAVL)
}
}
// Mount a store to the provided key in the BaseApp multistore
func (app *BaseApp) MountStoreWithDB(key sdk.StoreKey, typ sdk.StoreType, db dbm.DB) {
app.cms.MountStoreWithDB(key, typ, db)
}
// Mount a store to the provided key in the BaseApp multistore
func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) {
app.cms.MountStoreWithDB(key, typ, app.db)
@ -241,11 +248,6 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
// TODO Return something intelligent
panic(err)
}
err = app.Router().InitGenesis(app.deliverState.ctx, *genesisState)
if err != nil {
// TODO Return something intelligent
panic(err)
}
// NOTE: we don't commit, but BeginBlock for block 1
// starts from this deliverState

View File

@ -35,12 +35,15 @@ func TestMountStores(t *testing.T) {
// make some cap keys
capKey1 := sdk.NewKVStoreKey("key1")
db1 := dbm.NewMemDB()
capKey2 := sdk.NewKVStoreKey("key2")
db2 := dbm.NewMemDB()
// no stores are mounted
assert.Panics(t, func() { app.LoadLatestVersion(capKey1) })
app.MountStoresIAVL(capKey1, capKey2)
app.MountStoreWithDB(capKey1, sdk.StoreTypeIAVL, db1)
app.MountStoreWithDB(capKey2, sdk.StoreTypeIAVL, db2)
// stores are mounted
err := app.LoadLatestVersion(capKey1)
@ -126,7 +129,6 @@ func TestTxDecoder(t *testing.T) {
// Test that Info returns the latest committed state.
func TestInfo(t *testing.T) {
app := newBaseApp(t.Name())
// ----- test an empty response -------
@ -145,17 +147,19 @@ func TestInfo(t *testing.T) {
}
func TestInitChainer(t *testing.T) {
logger := defaultLogger()
db := dbm.NewMemDB()
name := t.Name()
db := dbm.NewMemDB()
logger := defaultLogger()
app := NewBaseApp(name, logger, db)
// make cap keys and mount the stores
// NOTE/TODO: mounting multiple stores is broken
// see https://github.com/cosmos/cosmos-sdk/issues/532
capKey := sdk.NewKVStoreKey("main")
// capKey2 := sdk.NewKVStoreKey("key2")
app.MountStoresIAVL(capKey) // , capKey2)
db1 := dbm.NewMemDB()
capKey2 := sdk.NewKVStoreKey("key2")
db2 := dbm.NewMemDB()
app.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db1)
app.MountStoreWithDB(capKey2, sdk.StoreTypeIAVL, db2)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
@ -187,9 +191,8 @@ func TestInitChainer(t *testing.T) {
// reload app
app = NewBaseApp(name, logger, db)
capKey = sdk.NewKVStoreKey("main")
// capKey2 = sdk.NewKVStoreKey("key2") // TODO
app.MountStoresIAVL(capKey) //, capKey2)
app.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db1)
app.MountStoreWithDB(capKey2, sdk.StoreTypeIAVL, db2)
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
app.SetInitChainer(initChainer)
@ -246,7 +249,7 @@ func TestDeliverTx(t *testing.T) {
counter += 1
return sdk.Result{}
}, nil)
})
tx := testUpdatePowerTx{} // doesn't matter
header := abci.Header{AppHash: []byte("apphash")}
@ -281,7 +284,7 @@ func TestQuery(t *testing.T) {
store := ctx.KVStore(capKey)
store.Set(key, value)
return sdk.Result{}
}, nil)
})
query := abci.RequestQuery{
Path: "/main/key",
@ -346,7 +349,7 @@ func TestValidatorChange(t *testing.T) {
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
// TODO
return sdk.Result{}
}, nil)
})
// Load latest state, which should be empty.
err := app.LoadLatestVersion(capKey)

View File

@ -1,8 +1,6 @@
package baseapp
import (
"encoding/json"
"fmt"
"regexp"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -10,16 +8,14 @@ import (
// Router provides handlers for each transaction type.
type Router interface {
AddRoute(r string, h sdk.Handler, i sdk.InitGenesis) (rtr Router)
AddRoute(r string, h sdk.Handler) (rtr Router)
Route(path string) (h sdk.Handler)
InitGenesis(ctx sdk.Context, data map[string]json.RawMessage) error
}
// map a transaction type to a handler and an initgenesis function
type route struct {
r string
h sdk.Handler
i sdk.InitGenesis
}
type router struct {
@ -38,11 +34,11 @@ func NewRouter() *router {
var isAlpha = regexp.MustCompile(`^[a-zA-Z]+$`).MatchString
// AddRoute - TODO add description
func (rtr *router) AddRoute(r string, h sdk.Handler, i sdk.InitGenesis) Router {
func (rtr *router) AddRoute(r string, h sdk.Handler) Router {
if !isAlpha(r) {
panic("route expressions can only contain alphanumeric characters")
}
rtr.routes = append(rtr.routes, route{r, h, i})
rtr.routes = append(rtr.routes, route{r, h})
return rtr
}
@ -57,20 +53,3 @@ func (rtr *router) Route(path string) (h sdk.Handler) {
}
return nil
}
// InitGenesis - call `InitGenesis`, where specified, for all routes
// Return the first error if any, otherwise nil
func (rtr *router) InitGenesis(ctx sdk.Context, data map[string]json.RawMessage) error {
for _, route := range rtr.routes {
if route.i != nil {
encoded, found := data[route.r]
if !found {
return sdk.ErrGenesisParse(fmt.Sprintf("Expected module genesis information for module %s but it was not present", route.r))
}
if err := route.i(ctx, encoded); err != nil {
return err
}
}
}
return nil
}

View File

@ -2,6 +2,7 @@ package keys
import (
"fmt"
"path/filepath"
"github.com/spf13/viper"
@ -32,7 +33,7 @@ type KeyOutput struct {
func GetKeyBase() (keys.Keybase, error) {
if keybase == nil {
rootDir := viper.GetString(cli.HomeFlag)
db, err := dbm.NewGoLevelDB(KeyDBName, rootDir)
db, err := dbm.NewGoLevelDB(KeyDBName, filepath.Join(rootDir, "keys"))
if err != nil {
return nil, err
}

View File

@ -1 +0,0 @@
tmp-base*

View File

@ -25,6 +25,7 @@ import (
ctypes "github.com/tendermint/tendermint/rpc/core/types"
tmrpc "github.com/tendermint/tendermint/rpc/lib/server"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -157,7 +158,7 @@ func TestNodeStatus(t *testing.T) {
func TestBlock(t *testing.T) {
time.Sleep(time.Second * 2) // TODO: LOL -> wait for blocks
waitForHeight(2)
var resultBlock ctypes.ResultBlock
@ -221,8 +222,7 @@ func TestCoinSend(t *testing.T) {
// create TX
receiveAddr, resultTx := doSend(t, port, seed)
time.Sleep(time.Second * 2) // T
waitForHeight(resultTx.Height + 1)
// check if tx was commited
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
@ -257,7 +257,7 @@ func TestIBCTransfer(t *testing.T) {
// create TX
resultTx := doIBCTransfer(t, port, seed)
time.Sleep(time.Second * 2) // T
waitForHeight(resultTx.Height + 1)
// check if tx was commited
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
@ -295,7 +295,7 @@ func TestTxs(t *testing.T) {
// create TX
_, resultTx := doSend(t, port, seed)
time.Sleep(time.Second * 2) // TO
waitForHeight(resultTx.Height + 1)
// check if tx is findable
res, body := request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
@ -320,6 +320,7 @@ func TestTxs(t *testing.T) {
// strt TM and the LCD in process, listening on their respective sockets
func startTMAndLCD() (*nm.Node, net.Listener, error) {
viper.Set(cli.HomeFlag, os.TempDir())
kb, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
if err != nil {
return nil, nil, err
@ -341,7 +342,13 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
logger = log.NewFilter(logger, log.AllowError())
privValidatorFile := config.PrivValidatorFile()
privVal := tmtypes.LoadOrGenPrivValidatorFS(privValidatorFile)
app := bapp.NewBasecoinApp(logger, dbm.NewMemDB())
dbs := map[string]dbm.DB{
"main": dbm.NewMemDB(),
"acc": dbm.NewMemDB(),
"ibc": dbm.NewMemDB(),
"staking": dbm.NewMemDB(),
}
app := bapp.NewBasecoinApp(logger, dbs)
genesisFile := config.GenesisFile()
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
@ -358,9 +365,6 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
Coins: coins,
},
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.Marshal(appState)
if err != nil {
@ -387,7 +391,7 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
return nil, nil, err
}
time.Sleep(time.Second * 2)
waitForStart()
return node, lcd, nil
}
@ -437,6 +441,7 @@ func request(t *testing.T, port, method, path string, payload []byte) (*http.Res
require.Nil(t, err)
output, err := ioutil.ReadAll(res.Body)
res.Body.Close()
require.Nil(t, err)
return res, string(output)
@ -456,8 +461,6 @@ func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctype
acc := auth.BaseAccount{}
err = json.Unmarshal([]byte(body), &acc)
require.Nil(t, err)
fmt.Println("BODY", body)
fmt.Println("ACC", acc)
sequence := acc.Sequence
// send
@ -485,8 +488,6 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
acc := auth.BaseAccount{}
err = json.Unmarshal([]byte(body), &acc)
require.Nil(t, err)
fmt.Println("BODY", body)
fmt.Println("ACC", acc)
sequence := acc.Sequence
// send
@ -499,3 +500,72 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
return resultTx
}
func waitForHeight(height int64) {
for {
var resultBlock ctypes.ResultBlock
url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest")
res, err := http.Get(url)
if err != nil {
panic(err)
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
res.Body.Close()
err = json.Unmarshal([]byte(body), &resultBlock)
if err != nil {
fmt.Println("RES", res)
fmt.Println("BODY", string(body))
panic(err)
}
if resultBlock.Block.Height >= height {
return
}
time.Sleep(time.Millisecond * 100)
}
}
// wait for 2 blocks
func waitForStart() {
waitHeight := int64(2)
for {
time.Sleep(time.Second)
var resultBlock ctypes.ResultBlock
url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest")
res, err := http.Get(url)
if err != nil {
panic(err)
}
// waiting for server to start ...
if res.StatusCode != http.StatusOK {
res.Body.Close()
continue
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
res.Body.Close()
err = json.Unmarshal([]byte(body), &resultBlock)
if err != nil {
fmt.Println("RES", res)
fmt.Println("BODY", string(body))
panic(err)
}
if resultBlock.Block.Height >= waitHeight {
return
}
}
}

View File

@ -18,8 +18,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/simplestake"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/sketchy"
)
const (
@ -33,6 +31,7 @@ type BasecoinApp struct {
// keys to access the substores
capKeyMainStore *sdk.KVStoreKey
capKeyAccountStore *sdk.KVStoreKey
capKeyIBCStore *sdk.KVStoreKey
capKeyStakingStore *sdk.KVStoreKey
@ -40,12 +39,13 @@ type BasecoinApp struct {
accountMapper sdk.AccountMapper
}
func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
func NewBasecoinApp(logger log.Logger, dbs map[string]dbm.DB) *BasecoinApp {
// create your application object
var app = &BasecoinApp{
BaseApp: bam.NewBaseApp(appName, logger, db),
BaseApp: bam.NewBaseApp(appName, logger, dbs["main"]),
cdc: MakeCodec(),
capKeyMainStore: sdk.NewKVStoreKey("main"),
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
}
@ -58,20 +58,22 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
// add handlers
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
app.Router().
AddRoute("bank", bank.NewHandler(coinKeeper), nil).
AddRoute("cool", cool.NewHandler(coolKeeper), coolKeeper.InitGenesis).
AddRoute("sketchy", sketchy.NewHandler(), nil).
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper), nil).
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper), nil)
AddRoute("bank", bank.NewHandler(coinKeeper)).
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
// initialize BaseApp
app.SetTxDecoder(app.txDecoder)
app.SetInitChainer(app.initChainer)
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyIBCStore, app.capKeyStakingStore)
app.MountStoreWithDB(app.capKeyMainStore, sdk.StoreTypeIAVL, dbs["main"])
app.MountStoreWithDB(app.capKeyAccountStore, sdk.StoreTypeIAVL, dbs["acc"])
app.MountStoreWithDB(app.capKeyIBCStore, sdk.StoreTypeIAVL, dbs["ibc"])
app.MountStoreWithDB(app.capKeyStakingStore, sdk.StoreTypeIAVL, dbs["staking"])
// NOTE: Broken until #532 lands
//app.MountStoresIAVL(app.capKeyMainStore, app.capKeyIBCStore, app.capKeyStakingStore)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper))
err := app.LoadLatestVersion(app.capKeyMainStore)
if err != nil {
@ -96,8 +98,6 @@ func MakeCodec() *wire.Codec {
struct{ sdk.Msg }{},
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
oldwire.ConcreteType{ibc.IBCTransferMsg{}, msgTypeIBCTransferMsg},
oldwire.ConcreteType{ibc.IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
oldwire.ConcreteType{simplestake.BondMsg{}, msgTypeBondMsg},

View File

@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
@ -26,50 +25,93 @@ import (
var (
chainID = "" // TODO
priv1 = crypto.GenPrivKeyEd25519()
addr1 = priv1.PubKey().Address()
addr2 = crypto.GenPrivKeyEd25519().PubKey().Address()
coins = sdk.Coins{{"foocoin", 10}}
fee = sdk.StdFee{
accName = "foobart"
priv1 = crypto.GenPrivKeyEd25519()
addr1 = priv1.PubKey().Address()
priv2 = crypto.GenPrivKeyEd25519()
addr2 = priv2.PubKey().Address()
addr3 = crypto.GenPrivKeyEd25519().PubKey().Address()
priv4 = crypto.GenPrivKeyEd25519()
addr4 = priv4.PubKey().Address()
coins = sdk.Coins{{"foocoin", 10}}
halfCoins = sdk.Coins{{"foocoin", 5}}
fee = sdk.StdFee{
sdk.Coins{{"foocoin", 0}},
0,
}
sendMsg = bank.SendMsg{
sendMsg1 = bank.SendMsg{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
}
quizMsg1 = cool.QuizMsg{
Sender: addr1,
CoolAnswer: "icecold",
sendMsg2 = bank.SendMsg{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{
bank.NewOutput(addr2, halfCoins),
bank.NewOutput(addr3, halfCoins),
},
}
quizMsg2 = cool.QuizMsg{
Sender: addr1,
CoolAnswer: "badvibesonly",
sendMsg3 = bank.SendMsg{
Inputs: []bank.Input{
bank.NewInput(addr1, coins),
bank.NewInput(addr4, coins),
},
Outputs: []bank.Output{
bank.NewOutput(addr2, coins),
bank.NewOutput(addr3, coins),
},
}
setTrendMsg1 = cool.SetTrendMsg{
Sender: addr1,
Cool: "icecold",
}
setTrendMsg2 = cool.SetTrendMsg{
Sender: addr1,
Cool: "badvibesonly",
}
setTrendMsg3 = cool.SetTrendMsg{
Sender: addr1,
Cool: "warmandkind",
sendMsg4 = bank.SendMsg{
Inputs: []bank.Input{
bank.NewInput(addr2, coins),
},
Outputs: []bank.Output{
bank.NewOutput(addr1, coins),
},
}
)
func newBasecoinApp() *BasecoinApp {
func loggerAndDBs() (log.Logger, map[string]dbm.DB) {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
db := dbm.NewMemDB()
return NewBasecoinApp(logger, db)
dbs := map[string]dbm.DB{
"main": dbm.NewMemDB(),
"acc": dbm.NewMemDB(),
"ibc": dbm.NewMemDB(),
"staking": dbm.NewMemDB(),
}
return logger, dbs
}
func newBasecoinApp() *BasecoinApp {
logger, dbs := loggerAndDBs()
return NewBasecoinApp(logger, dbs)
}
func setGenesisAccounts(bapp *BasecoinApp, accs ...auth.BaseAccount) error {
genaccs := make([]*types.GenesisAccount, len(accs))
for i, acc := range accs {
genaccs[i] = types.NewGenesisAccount(&types.AppAccount{acc, accName})
}
genesisState := types.GenesisState{
Accounts: genaccs,
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
if err != nil {
return err
}
// Initialize the chain
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
return nil
}
//_______________________________________________________________________
@ -80,41 +122,18 @@ func TestMsgs(t *testing.T) {
msgs := []struct {
msg sdk.Msg
}{
{sendMsg},
{quizMsg1},
{setTrendMsg1},
{sendMsg1},
}
sequences := []int64{0}
for i, m := range msgs {
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, m.msg))
tx := sdk.NewStdTx(m.msg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
}})
// just marshal/unmarshal!
cdc := MakeCodec()
txBytes, err := cdc.MarshalBinary(tx)
require.NoError(t, err, "i: %v", i)
// Run a Check
cres := bapp.CheckTx(txBytes)
assert.Equal(t, sdk.CodeUnknownAddress,
sdk.CodeType(cres.Code), "i: %v, log: %v", i, cres.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
dres := bapp.DeliverTx(txBytes)
assert.Equal(t, sdk.CodeUnknownAddress,
sdk.CodeType(dres.Code), "i: %v, log: %v", i, dres.Log)
// Run a CheckDeliver
SignCheckDeliver(t, bapp, m.msg, []int64{int64(i)}, false, priv1)
}
}
func TestGenesis(t *testing.T) {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
db := dbm.NewMemDB()
bapp := NewBasecoinApp(logger, db)
logger, dbs := loggerAndDBs()
bapp := NewBasecoinApp(logger, dbs)
// Construct some genesis bytes to reflect basecoin/types/AppAccount
pk := crypto.GenPrivKeyEd25519().PubKey()
@ -127,31 +146,19 @@ func TestGenesis(t *testing.T) {
}
acc := &types.AppAccount{baseAcc, "foobart"}
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
err = setGenesisAccounts(bapp, baseAcc)
assert.Nil(t, err)
// A checkTx context
ctx := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)
/*
// reload app and ensure the account is still there
bapp = NewBasecoinApp(logger, db)
ctx = bapp.BaseApp.NewContext(true, abci.Header{})
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)
*/
// reload app and ensure the account is still there
bapp = NewBasecoinApp(logger, dbs)
ctx = bapp.BaseApp.NewContext(true, abci.Header{})
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)
}
func TestSendMsgWithAccounts(t *testing.T) {
@ -165,69 +172,124 @@ func TestSendMsgWithAccounts(t *testing.T) {
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",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
// Initialize the chain
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
err = setGenesisAccounts(bapp, baseAcc)
assert.Nil(t, err)
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
assert.Equal(t, acc1, res1)
assert.Equal(t, baseAcc, res1.(*types.AppAccount).BaseAccount)
// Sign the tx
sequences := []int64{0}
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, sendMsg))
tx := sdk.NewStdTx(sendMsg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
}})
// Run a Check
res := bapp.Check(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
// Run a CheckDeliver
SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, true, priv1)
// Check balances
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
res3 := bapp.accountMapper.GetAccount(ctxDeliver, addr2)
assert.Equal(t, fmt.Sprintf("%v", res2.GetCoins()), "67foocoin")
assert.Equal(t, fmt.Sprintf("%v", res3.GetCoins()), "10foocoin")
CheckBalance(t, bapp, addr1, "67foocoin")
CheckBalance(t, bapp, addr2, "10foocoin")
// Delivering again should cause replay error
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeInvalidSequence, res.Code, res.Log)
SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, false, priv1)
// bumping the txnonce number without resigning should be an auth error
tx := genTx(sendMsg1, []int64{0}, priv1)
tx.Signatures[0].Sequence = 1
res = bapp.Deliver(tx)
res := bapp.Deliver(tx)
assert.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log)
// resigning the tx with the bumped sequence should work
sequences = []int64{1}
sig = priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, tx.Msg))
tx.Signatures[0].Signature = sig
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
SignCheckDeliver(t, bapp, sendMsg1, []int64{1}, true, priv1)
}
func TestSendMsgMultipleOut(t *testing.T) {
bapp := newBasecoinApp()
genCoins, err := sdk.ParseCoins("42foocoin")
require.Nil(t, err)
acc1 := auth.BaseAccount{
Address: addr1,
Coins: genCoins,
}
acc2 := auth.BaseAccount{
Address: addr2,
Coins: genCoins,
}
err = setGenesisAccounts(bapp, acc1, acc2)
assert.Nil(t, err)
// Simulate a Block
SignCheckDeliver(t, bapp, sendMsg2, []int64{0}, true, priv1)
// Check balances
CheckBalance(t, bapp, addr1, "32foocoin")
CheckBalance(t, bapp, addr2, "47foocoin")
CheckBalance(t, bapp, addr3, "5foocoin")
}
func TestSengMsgMultipleInOut(t *testing.T) {
bapp := newBasecoinApp()
genCoins, err := sdk.ParseCoins("42foocoin")
require.Nil(t, err)
acc1 := auth.BaseAccount{
Address: addr1,
Coins: genCoins,
}
acc2 := auth.BaseAccount{
Address: addr2,
Coins: genCoins,
}
acc4 := auth.BaseAccount{
Address: addr4,
Coins: genCoins,
}
err = setGenesisAccounts(bapp, acc1, acc2, acc4)
assert.Nil(t, err)
// CheckDeliver
SignCheckDeliver(t, bapp, sendMsg3, []int64{0, 0}, true, priv1, priv4)
// Check balances
CheckBalance(t, bapp, addr1, "32foocoin")
CheckBalance(t, bapp, addr4, "32foocoin")
CheckBalance(t, bapp, addr2, "52foocoin")
CheckBalance(t, bapp, addr3, "10foocoin")
}
func TestSendMsgDependent(t *testing.T) {
bapp := newBasecoinApp()
genCoins, err := sdk.ParseCoins("42foocoin")
require.Nil(t, err)
acc1 := auth.BaseAccount{
Address: addr1,
Coins: genCoins,
}
err = setGenesisAccounts(bapp, acc1)
assert.Nil(t, err)
// CheckDeliver
SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, true, priv1)
// Check balances
CheckBalance(t, bapp, addr1, "32foocoin")
CheckBalance(t, bapp, addr2, "10foocoin")
// Simulate a Block
SignCheckDeliver(t, bapp, sendMsg4, []int64{0}, true, priv2)
// Check balances
CheckBalance(t, bapp, addr1, "42foocoin")
}
func TestQuizMsg(t *testing.T) {
@ -247,9 +309,6 @@ func TestQuizMsg(t *testing.T) {
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc1),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
@ -264,48 +323,22 @@ func TestQuizMsg(t *testing.T) {
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
assert.Equal(t, acc1, res1)
// Set the trend, submit a really cool quiz and check for reward
SignCheckDeliver(t, bapp, setTrendMsg1, 0, true)
SignCheckDeliver(t, bapp, quizMsg1, 1, true)
CheckBalance(t, bapp, "69icecold")
SignCheckDeliver(t, bapp, quizMsg2, 2, false) // result without reward
CheckBalance(t, bapp, "69icecold")
SignCheckDeliver(t, bapp, quizMsg1, 3, true)
CheckBalance(t, bapp, "138icecold")
SignCheckDeliver(t, bapp, setTrendMsg2, 4, true) // reset the trend
SignCheckDeliver(t, bapp, quizMsg1, 5, false) // the same answer will nolonger do!
CheckBalance(t, bapp, "138icecold")
SignCheckDeliver(t, bapp, quizMsg2, 6, true) // earlier answer now relavent again
CheckBalance(t, bapp, "69badvibesonly,138icecold")
SignCheckDeliver(t, bapp, setTrendMsg3, 7, false) // expect to fail to set the trend to something which is not cool
}
func TestHandler(t *testing.T) {
func TestIBCMsgs(t *testing.T) {
bapp := newBasecoinApp()
sourceChain := "source-chain"
destChain := "dest-chain"
vals := []abci.Validator{}
baseAcc := auth.BaseAccount{
Address: addr1,
Coins: coins,
}
acc1 := &types.AppAccount{baseAcc, "foobart"}
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc1),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
err := setGenesisAccounts(bapp, baseAcc)
assert.Nil(t, err)
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
@ -329,24 +362,32 @@ func TestHandler(t *testing.T) {
Sequence: 0,
}
SignCheckDeliver(t, bapp, transferMsg, 0, true)
CheckBalance(t, bapp, "")
SignCheckDeliver(t, bapp, transferMsg, 1, false)
SignCheckDeliver(t, bapp, receiveMsg, 2, true)
CheckBalance(t, bapp, "10foocoin")
SignCheckDeliver(t, bapp, receiveMsg, 3, false)
SignCheckDeliver(t, bapp, transferMsg, []int64{0}, true, priv1)
CheckBalance(t, bapp, addr1, "")
SignCheckDeliver(t, bapp, transferMsg, []int64{1}, false, priv1)
SignCheckDeliver(t, bapp, receiveMsg, []int64{2}, true, priv1)
CheckBalance(t, bapp, addr1, "10foocoin")
SignCheckDeliver(t, bapp, receiveMsg, []int64{3}, false, priv1)
}
// TODO describe the use of this function
func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq int64, expPass bool) {
func genTx(msg sdk.Msg, seq []int64, priv ...crypto.PrivKeyEd25519) sdk.StdTx {
sigs := make([]sdk.StdSignature, len(priv))
for i, p := range priv {
sigs[i] = sdk.StdSignature{
PubKey: p.PubKey(),
Signature: p.Sign(sdk.StdSignBytes(chainID, seq, fee, msg)),
Sequence: seq[i],
}
}
return sdk.NewStdTx(msg, fee, sigs)
}
func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) {
// Sign the tx
tx := sdk.NewStdTx(msg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: priv1.Sign(sdk.StdSignBytes(chainID, []int64{seq}, fee, msg)),
Sequence: seq,
}})
tx := genTx(msg, seq, priv...)
// Run a Check
res := bapp.Check(tx)
if expPass {
@ -367,8 +408,8 @@ func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq int64, e
//bapp.Commit()
}
func CheckBalance(t *testing.T, bapp *BasecoinApp, balExpected string) {
func CheckBalance(t *testing.T, bapp *BasecoinApp, addr sdk.Address, balExpected string) {
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr)
assert.Equal(t, balExpected, fmt.Sprintf("%v", res2.GetCoins()))
}

View File

@ -14,7 +14,6 @@ import (
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
coolcmd "github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool/commands"
"github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
@ -63,14 +62,6 @@ func main() {
client.PostCommands(
bankcmd.SendTxCmd(cdc),
)...)
basecliCmd.AddCommand(
client.PostCommands(
coolcmd.QuizTxCmd(cdc),
)...)
basecliCmd.AddCommand(
client.PostCommands(
coolcmd.SetTrendTxCmd(cdc),
)...)
basecliCmd.AddCommand(
client.PostCommands(
ibccmd.IBCTransferCmd(cdc),

View File

@ -10,6 +10,7 @@ import (
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/cli"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -28,14 +29,11 @@ var (
// defaultOptions sets up the app_options for the
// default genesis file
func defaultOptions(args []string) (json.RawMessage, error) {
func defaultOptions(args []string) (json.RawMessage, string, cmn.HexBytes, error) {
addr, secret, err := server.GenerateCoinKey()
if err != nil {
return nil, err
return nil, "", nil, err
}
fmt.Println("Secret phrase to access coins:")
fmt.Println(secret)
opts := fmt.Sprintf(`{
"accounts": [{
"address": "%s",
@ -47,15 +45,33 @@ func defaultOptions(args []string) (json.RawMessage, error) {
]
}]
}`, addr)
return json.RawMessage(opts), nil
return json.RawMessage(opts), secret, addr, nil
}
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
db, err := dbm.NewGoLevelDB("basecoin", filepath.Join(rootDir, "data"))
dbMain, err := dbm.NewGoLevelDB("basecoin", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
bapp := app.NewBasecoinApp(logger, db)
dbAcc, err := dbm.NewGoLevelDB("basecoin-acc", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbIBC, err := dbm.NewGoLevelDB("basecoin-ibc", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbStaking, err := dbm.NewGoLevelDB("basecoin-staking", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbs := map[string]dbm.DB{
"main": dbMain,
"acc": dbAcc,
"ibc": dbIBC,
"staking": dbStaking,
}
bapp := app.NewBasecoinApp(logger, dbs)
return bapp, nil
}

204
examples/democoin/LICENSE Normal file
View File

@ -0,0 +1,204 @@
Cosmos-SDK Democoin (template)
License: Apache2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 All in Bits, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,22 @@
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/examples/democoin/version.GitCommit=`git rev-parse --short HEAD`"
all: get_tools get_vendor_deps build test
get_tools:
go get github.com/golang/dep/cmd/dep
build:
go build $(BUILD_FLAGS) -o build/democoin ./cmd/...
get_vendor_deps:
@rm -rf vendor/
@dep ensure
test:
@go test $(PACKAGES)
benchmark:
@go test -bench=. $(PACKAGES)
.PHONY: all build test benchmark

View File

@ -0,0 +1,70 @@
# Democoin
This is the "Democoin" example application built on the Cosmos-Sdk. This
"Democoin" is not affiliated with [Coinbase](http://www.getdemocoin.com/), nor
the [stable coin](http://www.getdemocoin.com/).
Assuming you've run `make get_tools && make get_vendor_deps` from the root of
this repository, run `make build` here to build the `democoind` and `basecli`
binaries.
If you want to create a new application, start by copying the Democoin app.
# Building your own Blockchain
Democoin is the equivalent of an ERC20 token contract for blockchains. In order
to deploy your own application all you need to do is clone `examples/democoin`
and run it. Now you are already running your own blockchain. In the following
I will explain how to add functionality to your blockchain. This is akin to
defining your own vesting schedule within a contract or setting a specific
multisig. You are just extending the base layer with extra functionality here
and there.
## Structure of Democoin
Democoin is build with the cosmos-sdk. It is a sample application that works
with any engine that implements the ABCI protocol. Democoin defines multiple
unique modules as well as uses modules directly from the sdk. If you want
to modify Democoin, you either remove or add modules according to your wishes.
## Modules
A module is a fundamental unit in the cosmos-sdk. A module defines its own
transaction, handles its own state as well as its own state transition logic.
Globally, in the `app/app.go` file you just have to define a key for that
module to access some parts of the state, as well as initialise the module
object and finally add it to the transaction router. The router ensures that
every module only gets its own messages.
## Transactions
A user can send a transaction to the running blockchain application. This
transaction can be of any of the ones that are supported by any of the
registered modules.
### CheckTx
Once a user has submitted their transaction to the engine,
the engine will first run `checkTx` to confirm that it is a valid transaction.
The module has to define a handler that knows how to handle every transaction
type. The corresponding handler gets invoked with the checkTx flag set to true.
This means that the handler shouldn't do any expensive operations, but it can
and should write to the checkTx state.
### DeliverTx
The engine calls `deliverTx` when a new block has been agreed upon in
consensus. Again, the corresponding module will have its handler invoked
and the state and context is passed in. During deliverTx execution the
transaction needs to be processed fully and the results are written to the
application state.
## CLI
The cosmos-sdk contains a number of helper libraries in `clients/` to build cli
and RPC interfaces for your specific application.

View File

@ -0,0 +1,175 @@
package app
import (
"encoding/json"
abci "github.com/tendermint/abci/types"
oldwire "github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
bam "github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/simplestake"
"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/sketchy"
)
const (
appName = "DemocoinApp"
)
// Extended ABCI application
type DemocoinApp struct {
*bam.BaseApp
cdc *wire.Codec
// keys to access the substores
capKeyMainStore *sdk.KVStoreKey
capKeyAccountStore *sdk.KVStoreKey
capKeyIBCStore *sdk.KVStoreKey
capKeyStakingStore *sdk.KVStoreKey
// Manage getting and setting accounts
accountMapper sdk.AccountMapper
}
func NewDemocoinApp(logger log.Logger, dbs map[string]dbm.DB) *DemocoinApp {
// create your application object
var app = &DemocoinApp{
BaseApp: bam.NewBaseApp(appName, logger, dbs["main"]),
cdc: MakeCodec(),
capKeyMainStore: sdk.NewKVStoreKey("main"),
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
}
// define the accountMapper
app.accountMapper = auth.NewAccountMapperSealed(
app.capKeyMainStore, // target store
&types.AppAccount{}, // prototype
)
// add handlers
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
coolKeeper := cool.NewKeeper(app.capKeyMainStore, 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("sketchy", sketchy.NewHandler()).
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
// initialize BaseApp
app.SetTxDecoder(app.txDecoder)
app.SetInitChainer(app.initChainerFn(coolKeeper))
app.MountStoreWithDB(app.capKeyMainStore, sdk.StoreTypeIAVL, dbs["main"])
app.MountStoreWithDB(app.capKeyAccountStore, sdk.StoreTypeIAVL, dbs["acc"])
app.MountStoreWithDB(app.capKeyIBCStore, sdk.StoreTypeIAVL, dbs["ibc"])
app.MountStoreWithDB(app.capKeyStakingStore, sdk.StoreTypeIAVL, dbs["staking"])
// NOTE: Broken until #532 lands
//app.MountStoresIAVL(app.capKeyMainStore, app.capKeyIBCStore, app.capKeyStakingStore)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper))
err := app.LoadLatestVersion(app.capKeyMainStore)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
// custom tx codec
// TODO: use new go-wire
func MakeCodec() *wire.Codec {
const msgTypeSend = 0x1
const msgTypeIssue = 0x2
const msgTypeQuiz = 0x3
const msgTypeSetTrend = 0x4
const msgTypeIBCTransferMsg = 0x5
const msgTypeIBCReceiveMsg = 0x6
const msgTypeBondMsg = 0x7
const msgTypeUnbondMsg = 0x8
var _ = oldwire.RegisterInterface(
struct{ sdk.Msg }{},
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
oldwire.ConcreteType{ibc.IBCTransferMsg{}, msgTypeIBCTransferMsg},
oldwire.ConcreteType{ibc.IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
oldwire.ConcreteType{simplestake.BondMsg{}, msgTypeBondMsg},
oldwire.ConcreteType{simplestake.UnbondMsg{}, msgTypeUnbondMsg},
)
const accTypeApp = 0x1
var _ = oldwire.RegisterInterface(
struct{ sdk.Account }{},
oldwire.ConcreteType{&types.AppAccount{}, accTypeApp},
)
cdc := wire.NewCodec()
// cdc.RegisterInterface((*sdk.Msg)(nil), nil)
// bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types.
// crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types.
// ibc.RegisterWire(cdc) // Register ibc.[IBCTransferMsg, IBCReceiveMsg] types.
return cdc
}
// custom logic for transaction decoding
func (app *DemocoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx = sdk.StdTx{}
if len(txBytes) == 0 {
return nil, sdk.ErrTxDecode("txBytes are empty")
}
// StdTx.Msg is an interface. The concrete types
// are registered by MakeTxCodec in bank.RegisterWire.
err := app.cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
}
return tx, nil
}
// custom logic for democoin initialization
func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes
genesisState := new(types.GenesisState)
err := json.Unmarshal(stateJSON, genesisState)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
}
for _, gacc := range genesisState.Accounts {
acc, err := gacc.ToAppAccount()
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
}
app.accountMapper.SetAccount(ctx, acc)
}
// Application specific genesis handling
err = coolKeeper.InitGenesis(ctx, stateJSON)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
}
return abci.ResponseInitChain{}
}
}

View File

@ -0,0 +1,382 @@
package app
import (
"encoding/json"
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
)
// Construct some global addrs and txs for tests.
var (
chainID = "" // TODO
priv1 = crypto.GenPrivKeyEd25519()
addr1 = priv1.PubKey().Address()
addr2 = crypto.GenPrivKeyEd25519().PubKey().Address()
coins = sdk.Coins{{"foocoin", 10}}
fee = sdk.StdFee{
sdk.Coins{{"foocoin", 0}},
0,
}
sendMsg = bank.SendMsg{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
}
quizMsg1 = cool.QuizMsg{
Sender: addr1,
CoolAnswer: "icecold",
}
quizMsg2 = cool.QuizMsg{
Sender: addr1,
CoolAnswer: "badvibesonly",
}
setTrendMsg1 = cool.SetTrendMsg{
Sender: addr1,
Cool: "icecold",
}
setTrendMsg2 = cool.SetTrendMsg{
Sender: addr1,
Cool: "badvibesonly",
}
setTrendMsg3 = cool.SetTrendMsg{
Sender: addr1,
Cool: "warmandkind",
}
)
func loggerAndDBs() (log.Logger, map[string]dbm.DB) {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
dbs := map[string]dbm.DB{
"main": dbm.NewMemDB(),
"acc": dbm.NewMemDB(),
"ibc": dbm.NewMemDB(),
"staking": dbm.NewMemDB(),
}
return logger, dbs
}
func newDemocoinApp() *DemocoinApp {
logger, dbs := loggerAndDBs()
return NewDemocoinApp(logger, dbs)
}
//_______________________________________________________________________
func TestMsgs(t *testing.T) {
bapp := newDemocoinApp()
msgs := []struct {
msg sdk.Msg
}{
{sendMsg},
{quizMsg1},
{setTrendMsg1},
}
sequences := []int64{0}
for i, m := range msgs {
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, m.msg))
tx := sdk.NewStdTx(m.msg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
}})
// just marshal/unmarshal!
cdc := MakeCodec()
txBytes, err := cdc.MarshalBinary(tx)
require.NoError(t, err, "i: %v", i)
// Run a Check
cres := bapp.CheckTx(txBytes)
assert.Equal(t, sdk.CodeUnknownAddress,
sdk.CodeType(cres.Code), "i: %v, log: %v", i, cres.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
dres := bapp.DeliverTx(txBytes)
assert.Equal(t, sdk.CodeUnknownAddress,
sdk.CodeType(dres.Code), "i: %v, log: %v", i, dres.Log)
}
}
func TestGenesis(t *testing.T) {
logger, dbs := loggerAndDBs()
bapp := NewDemocoinApp(logger, dbs)
// Construct some genesis bytes to reflect democoin/types/AppAccount
pk := crypto.GenPrivKeyEd25519().PubKey()
addr := pk.Address()
coins, err := sdk.ParseCoins("77foocoin,99barcoin")
require.Nil(t, err)
baseAcc := auth.BaseAccount{
Address: addr,
Coins: coins,
}
acc := &types.AppAccount{baseAcc, "foobart"}
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
// A checkTx context
ctx := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)
// reload app and ensure the account is still there
bapp = NewDemocoinApp(logger, dbs)
ctx = bapp.BaseApp.NewContext(true, abci.Header{})
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)
}
func TestSendMsgWithAccounts(t *testing.T) {
bapp := newDemocoinApp()
// Construct some genesis bytes to reflect democoin/types/AppAccount
// Give 77 foocoin to the first key
coins, err := sdk.ParseCoins("77foocoin")
require.Nil(t, err)
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",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
// Initialize the chain
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)
// Sign the tx
sequences := []int64{0}
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, sendMsg))
tx := sdk.NewStdTx(sendMsg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
}})
// Run a Check
res := bapp.Check(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
// Check balances
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
res3 := bapp.accountMapper.GetAccount(ctxDeliver, addr2)
assert.Equal(t, fmt.Sprintf("%v", res2.GetCoins()), "67foocoin")
assert.Equal(t, fmt.Sprintf("%v", res3.GetCoins()), "10foocoin")
// Delivering again should cause replay error
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeInvalidSequence, res.Code, res.Log)
// bumping the txnonce number without resigning should be an auth error
tx.Signatures[0].Sequence = 1
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log)
// resigning the tx with the bumped sequence should work
sequences = []int64{1}
sig = priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, tx.Msg))
tx.Signatures[0].Signature = sig
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
}
func TestQuizMsg(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",
},
}
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)
// Set the trend, submit a really cool quiz and check for reward
SignCheckDeliver(t, bapp, setTrendMsg1, 0, true)
SignCheckDeliver(t, bapp, quizMsg1, 1, true)
CheckBalance(t, bapp, "69icecold")
SignCheckDeliver(t, bapp, quizMsg2, 2, false) // result without reward
CheckBalance(t, bapp, "69icecold")
SignCheckDeliver(t, bapp, quizMsg1, 3, true)
CheckBalance(t, bapp, "138icecold")
SignCheckDeliver(t, bapp, setTrendMsg2, 4, true) // reset the trend
SignCheckDeliver(t, bapp, quizMsg1, 5, false) // the same answer will nolonger do!
CheckBalance(t, bapp, "138icecold")
SignCheckDeliver(t, bapp, quizMsg2, 6, true) // earlier answer now relavent again
CheckBalance(t, bapp, "69badvibesonly,138icecold")
SignCheckDeliver(t, bapp, setTrendMsg3, 7, false) // expect to fail to set the trend to something which is not cool
}
func TestHandler(t *testing.T) {
bapp := newDemocoinApp()
sourceChain := "source-chain"
destChain := "dest-chain"
vals := []abci.Validator{}
baseAcc := auth.BaseAccount{
Address: addr1,
Coins: coins,
}
acc1 := &types.AppAccount{baseAcc, "foobart"}
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc1),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
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)
packet := ibc.IBCPacket{
SrcAddr: addr1,
DestAddr: addr1,
Coins: coins,
SrcChain: sourceChain,
DestChain: destChain,
}
transferMsg := ibc.IBCTransferMsg{
IBCPacket: packet,
}
receiveMsg := ibc.IBCReceiveMsg{
IBCPacket: packet,
Relayer: addr1,
Sequence: 0,
}
SignCheckDeliver(t, bapp, transferMsg, 0, true)
CheckBalance(t, bapp, "")
SignCheckDeliver(t, bapp, transferMsg, 1, false)
SignCheckDeliver(t, bapp, receiveMsg, 2, true)
CheckBalance(t, bapp, "10foocoin")
SignCheckDeliver(t, bapp, receiveMsg, 3, false)
}
// TODO describe the use of this function
func SignCheckDeliver(t *testing.T, bapp *DemocoinApp, msg sdk.Msg, seq int64, expPass bool) {
// Sign the tx
tx := sdk.NewStdTx(msg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: priv1.Sign(sdk.StdSignBytes(chainID, []int64{seq}, fee, msg)),
Sequence: seq,
}})
// Run a Check
res := bapp.Check(tx)
if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
}
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx)
if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
}
bapp.EndBlock(abci.RequestEndBlock{})
//bapp.Commit()
}
func CheckBalance(t *testing.T, bapp *DemocoinApp, balExpected string) {
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
assert.Equal(t, balExpected, fmt.Sprintf("%v", res2.GetCoins()))
}

View File

@ -0,0 +1,91 @@
package main
import (
"errors"
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/lcd"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands"
simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands"
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
)
// gaiacliCmd is the entry point for this binary
var (
democliCmd = &cobra.Command{
Use: "democli",
Short: "Democoin light-client",
}
)
func todoNotImplemented(_ *cobra.Command, _ []string) error {
return errors.New("TODO: Command not yet implemented")
}
func main() {
// disable sorting
cobra.EnableCommandSorting = false
// get the codec
cdc := app.MakeCodec()
// TODO: setup keybase, viper object, etc. to be passed into
// the below functions and eliminate global vars, like we do
// with the cdc
// add standard rpc, and tx commands
rpc.AddCommands(democliCmd)
democliCmd.AddCommand(client.LineBreak)
tx.AddCommands(democliCmd, cdc)
democliCmd.AddCommand(client.LineBreak)
// add query/post commands (custom to binary)
democliCmd.AddCommand(
client.GetCommands(
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
)...)
democliCmd.AddCommand(
client.PostCommands(
bankcmd.SendTxCmd(cdc),
)...)
democliCmd.AddCommand(
client.PostCommands(
ibccmd.IBCTransferCmd(cdc),
)...)
democliCmd.AddCommand(
client.PostCommands(
ibccmd.IBCRelayCmd(cdc),
simplestakingcmd.BondTxCmd(cdc),
)...)
democliCmd.AddCommand(
client.PostCommands(
simplestakingcmd.UnbondTxCmd(cdc),
)...)
// add proxy, version and key info
democliCmd.AddCommand(
client.LineBreak,
lcd.ServeCommand(cdc),
keys.Commands(),
client.LineBreak,
version.VersionCmd,
)
// prepare and add flags
executor := cli.PrepareMainCmd(democliCmd, "BC", os.ExpandEnv("$HOME/.democli"))
executor.Execute()
}

View File

@ -0,0 +1,99 @@
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/spf13/cobra"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/cli"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/version"
)
// democoindCmd is the entry point for this binary
var (
democoindCmd = &cobra.Command{
Use: "democoind",
Short: "Gaia Daemon (server)",
}
)
// defaultOptions sets up the app_options for the
// default genesis file
func defaultOptions(args []string) (json.RawMessage, string, cmn.HexBytes, error) {
addr, secret, err := server.GenerateCoinKey()
if err != nil {
return nil, "", nil, err
}
fmt.Println("Secret phrase to access coins:")
fmt.Println(secret)
opts := fmt.Sprintf(`{
"accounts": [{
"address": "%s",
"coins": [
{
"denom": "mycoin",
"amount": 9007199254740992
}
]
}]
}`, addr)
return json.RawMessage(opts), "", nil, nil
}
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
dbMain, err := dbm.NewGoLevelDB("democoin", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbAcc, err := dbm.NewGoLevelDB("democoin-acc", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbIBC, err := dbm.NewGoLevelDB("democoin-ibc", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbStaking, err := dbm.NewGoLevelDB("democoin-staking", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbs := map[string]dbm.DB{
"main": dbMain,
"acc": dbAcc,
"ibc": dbIBC,
"staking": dbStaking,
}
bapp := app.NewDemocoinApp(logger, dbs)
return bapp, nil
}
func main() {
// TODO: set logger through CLI
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
With("module", "main")
democoindCmd.AddCommand(
server.InitCmd(defaultOptions, logger),
server.StartCmd(generateApp, logger),
server.UnsafeResetAllCmd(logger),
server.ShowNodeIdCmd(logger),
server.ShowValidatorCmd(logger),
version.VersionCmd,
)
// prepare and add flags
rootDir := os.ExpandEnv("$HOME/.democoind")
executor := cli.PrepareBaseCmd(democoindCmd, "BC", rootDir)
executor.Execute()
}

View File

@ -0,0 +1,72 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
)
var _ sdk.Account = (*AppAccount)(nil)
// Custom extensions for this application. This is just an example of
// extending auth.BaseAccount with custom fields.
//
// This is compatible with the stock auth.AccountStore, since
// auth.AccountStore uses the flexible go-wire library.
type AppAccount struct {
auth.BaseAccount
Name string `json:"name"`
}
// nolint
func (acc AppAccount) GetName() string { return acc.Name }
func (acc *AppAccount) SetName(name string) { acc.Name = name }
// Get the AccountDecoder function for the custom AppAccount
func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder {
return func(accBytes []byte) (res sdk.Account, err error) {
if len(accBytes) == 0 {
return nil, sdk.ErrTxDecode("accBytes are empty")
}
acct := new(AppAccount)
err = cdc.UnmarshalBinary(accBytes, &acct)
if err != nil {
panic(err)
}
return acct, err
}
}
//___________________________________________________________________________________
// State to Unmarshal
type GenesisState struct {
Accounts []*GenesisAccount `json:"accounts"`
}
// GenesisAccount doesn't need pubkey or sequence
type GenesisAccount struct {
Name string `json:"name"`
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
}
func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
return &GenesisAccount{
Name: aa.Name,
Address: aa.Address,
Coins: aa.Coins,
}
}
// convert GenesisAccount to AppAccount
func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) {
baseAcc := auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins,
}
return &AppAccount{
BaseAccount: baseAcc,
Name: ga.Name,
}, nil
}

View File

@ -11,7 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
)
// take the coolness quiz transaction

View File

@ -9,6 +9,7 @@ import (
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/cli"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/baseapp"
@ -26,10 +27,10 @@ var (
// defaultOptions sets up the app_options for the
// default genesis file
func defaultOptions(args []string) (json.RawMessage, error) {
func defaultOptions(args []string) (json.RawMessage, string, cmn.HexBytes, error) {
addr, secret, err := server.GenerateCoinKey()
if err != nil {
return nil, err
return nil, "", nil, err
}
fmt.Println("Secret phrase to access coins:")
fmt.Println(secret)
@ -45,7 +46,7 @@ func defaultOptions(args []string) (json.RawMessage, error) {
]
}]
}`, addr)
return json.RawMessage(opts), nil
return json.RawMessage(opts), secret, addr, nil
}
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {

View File

@ -3,8 +3,12 @@ package main
import (
"fmt"
"os"
"path/filepath"
"github.com/spf13/viper"
"github.com/tendermint/abci/server"
"github.com/tendermint/tmlibs/cli"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -17,7 +21,8 @@ func main() {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main")
db, err := dbm.NewGoLevelDB("basecoind", "data")
rootDir := viper.GetString(cli.HomeFlag)
db, err := dbm.NewGoLevelDB("basecoind", filepath.Join(rootDir, "data"))
if err != nil {
fmt.Println(err)
os.Exit(1)
@ -30,13 +35,13 @@ func main() {
var baseApp = bam.NewBaseApp("kvstore", logger, db)
// Set mounts for BaseApp's MultiStore.
baseApp.MountStore(capKeyMainStore, sdk.StoreTypeIAVL)
baseApp.MountStoresIAVL(capKeyMainStore)
// Set Tx decoder
baseApp.SetTxDecoder(decodeTx)
// Set a handler Route.
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore), nil)
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore))
// Load latest version.
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {

View File

@ -6,6 +6,7 @@ import (
"path/filepath"
abci "github.com/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -38,7 +39,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) {
baseApp.SetInitChainer(InitChainer(capKeyMainStore))
// Set a handler Route.
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore), nil)
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore))
// Load latest version.
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
@ -106,7 +107,7 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci
// GenInitOptions can be passed into InitCmd,
// returns a static string of a few key-values that can be parsed
// by InitChainer
func GenInitOptions(args []string) (json.RawMessage, error) {
func GenInitOptions(args []string) (json.RawMessage, string, cmn.HexBytes, error) {
opts := []byte(`{
"values": [
{
@ -119,5 +120,5 @@ func GenInitOptions(args []string) (json.RawMessage, error) {
}
]
}`)
return opts, nil
return opts, "", nil, nil
}

View File

@ -21,7 +21,7 @@ func TestInitApp(t *testing.T) {
require.NoError(t, err)
// initialize it future-way
opts, err := GenInitOptions(nil)
opts, _, _, err := GenInitOptions(nil)
require.NoError(t, err)
req := abci.RequestInitChain{AppStateBytes: opts}
app.InitChain(req)

View File

@ -2,6 +2,7 @@ package server
import (
"encoding/json"
"fmt"
"io/ioutil"
"github.com/spf13/cobra"
@ -11,9 +12,17 @@ import (
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p"
tmtypes "github.com/tendermint/tendermint/types"
)
type testnetInformation struct {
Secret string `json:"secret"`
Account string `json:"account"`
Validator tmtypes.GenesisValidator `json:"validator"`
NodeID p2p.ID `json:"node_id"`
}
// InitCmd will initialize all files for tendermint,
// along with proper app_state.
// The application can pass in a function to generate
@ -24,17 +33,19 @@ func InitCmd(gen GenAppState, logger log.Logger) *cobra.Command {
genAppState: gen,
logger: logger,
}
return &cobra.Command{
cobraCmd := cobra.Command{
Use: "init",
Short: "Initialize genesis files",
RunE: cmd.run,
}
return &cobraCmd
}
// GenAppState can parse command-line and flag to
// GenAppState can parse command-line to
// generate default app_state for the genesis file.
// Also must return generated seed and address
// This is application-specific
type GenAppState func(args []string) (json.RawMessage, error)
type GenAppState func(args []string) (json.RawMessage, string, cmn.HexBytes, error)
type initCmd struct {
genAppState GenAppState
@ -42,13 +53,16 @@ type initCmd struct {
}
func (c initCmd) run(cmd *cobra.Command, args []string) error {
// Store testnet information as we go
var testnetInfo testnetInformation
// Run the basic tendermint initialization,
// set up a default genesis with no app_options
config, err := tcmd.ParseConfig()
if err != nil {
return err
}
err = c.initTendermintFiles(config)
err = c.initTendermintFiles(config, &testnetInfo)
if err != nil {
return err
}
@ -59,19 +73,36 @@ func (c initCmd) run(cmd *cobra.Command, args []string) error {
}
// Now, we want to add the custom app_state
appState, err := c.genAppState(args)
appState, secret, address, err := c.genAppState(args)
if err != nil {
return err
}
testnetInfo.Secret = secret
testnetInfo.Account = address.String()
// And add them to the genesis file
genFile := config.GenesisFile()
return addGenesisState(genFile, appState)
if err := addGenesisState(genFile, appState); err != nil {
return err
}
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return err
}
testnetInfo.NodeID = nodeKey.ID()
out, err := json.MarshalIndent(testnetInfo, "", " ")
if err != nil {
return err
}
fmt.Println(string(out))
return nil
}
// This was copied from tendermint/cmd/tendermint/commands/init.go
// so we could pass in the config and the logger.
func (c initCmd) initTendermintFiles(config *cfg.Config) error {
func (c initCmd) initTendermintFiles(config *cfg.Config, info *testnetInformation) error {
// private validator
privValFile := config.PrivValidatorFile()
var privValidator *tmtypes.PrivValidatorFS
@ -102,6 +133,18 @@ func (c initCmd) initTendermintFiles(config *cfg.Config) error {
}
c.logger.Info("Generated genesis file", "path", genFile)
}
// reload the config file and find our validator info
loadedDoc, err := tmtypes.GenesisDocFromFile(genFile)
if err != nil {
return err
}
for _, validator := range loadedDoc.Validators {
if validator.PubKey == privValidator.GetPubKey() {
info.Validator = validator
}
}
return nil
}

View File

@ -6,10 +6,10 @@ package version
// TODO improve
const Maj = "0"
const Min = "12"
const Min = "13"
const Fix = "0"
const Version = "0.12.0"
const Version = "0.13.0"
// GitCommit set by build flags
var GitCommit = ""

View File

@ -15,8 +15,6 @@ import (
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
)
// AccountMapper(/CoinKeeper) and IBCMapper should use different StoreKey later
@ -53,8 +51,6 @@ func makeCodec() *wire.Codec {
struct{ sdk.Msg }{},
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
oldwire.ConcreteType{IBCTransferMsg{}, msgTypeIBCTransferMsg},
oldwire.ConcreteType{IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
)

View File

@ -71,17 +71,6 @@ func NewHandler(k Keeper, ck bank.CoinKeeper) sdk.Handler {
//_____________________________________________________________________
// XXX should be send in the msg (init in CLI)
//func getSender() sdk.Address {
//signers := msg.GetSigners()
//if len(signers) != 1 {
//return sdk.ErrUnauthorized("there can only be one signer for staking transaction").Result()
//}
//sender := signers[0]
//}
//_____________________________________________________________________
// These functions assume everything has been authenticated,
// now we just perform action and save
@ -187,8 +176,11 @@ func BondCoins(ctx sdk.Context, k Keeper, bond DelegatorBond, candidate Candidat
if err != nil {
return err
}
newShares := k.candidateAddTokens(ctx, candidate, amount.Amount)
p := k.GetPool(ctx)
p, candidate, newShares := p.candidateAddTokens(candidate, amount.Amount)
bond.Shares = bond.Shares.Add(newShares)
k.setPool(ctx, p)
k.setCandidate(ctx, candidate)
k.setDelegatorBond(ctx, bond)
return nil
}
@ -258,7 +250,9 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
}
// Add the coins
returnAmount := k.candidateRemoveShares(ctx, candidate, shares)
p := k.GetPool(ctx)
var returnAmount int64
p, candidate, returnAmount = p.candidateRemoveShares(candidate, shares)
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
k.coinKeeper.AddCoins(ctx, bond.DelegatorAddr, returnCoins)
@ -267,7 +261,7 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
// change the share types to unbonded if they were not already
if candidate.Status == Bonded {
k.bondedToUnbondedPool(ctx, candidate)
p, candidate = p.bondedToUnbondedPool(candidate)
}
// lastly update the status
@ -280,6 +274,7 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
} else {
k.setCandidate(ctx, candidate)
}
k.setPool(ctx, p)
return sdk.Result{}
}
@ -293,12 +288,16 @@ func UnbondCoins(ctx sdk.Context, k Keeper, bond DelegatorBond, candidate Candid
}
bond.Shares = bond.Shares.Sub(shares)
returnAmount := k.candidateRemoveShares(ctx, candidate, shares)
p := k.GetPool(ctx)
var returnAmount int64
p, candidate, returnAmount = p.candidateRemoveShares(candidate, shares)
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
_, err := k.coinKeeper.AddCoins(ctx, candidate.Address, returnCoins)
if err != nil {
return err
}
k.setPool(ctx, p)
k.setCandidate(ctx, candidate)
return nil
}

View File

@ -1,248 +1,245 @@
package stake
//import (
//"strconv"
//"testing"
/*
import (
"strconv"
"testing"
//"github.com/stretchr/testify/assert"
//"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
//crypto "github.com/tendermint/go-crypto"
crypto "github.com/tendermint/go-crypto"
//sdk "github.com/cosmos/cosmos-sdk/types"
//)
sdk "github.com/cosmos/cosmos-sdk/types"
)
////______________________________________________________________________
//______________________________________________________________________
//func newTestMsgDeclareCandidacy(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgDeclareCandidacy {
//return MsgDeclareCandidacy{
//Description: Description{},
//CandidateAddr: address,
//Bond: sdk.Coin{"fermion", amt},
//PubKey: pubKey,
//}
//}
func newTestMsgDeclareCandidacy(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgDeclareCandidacy {
return MsgDeclareCandidacy{
Description: Description{},
CandidateAddr: address,
Bond: sdk.Coin{"fermion", amt},
PubKey: pubKey,
}
}
//func newTestMsgDelegate(amt int64, delegatorAddr, candidateAddr sdk.Address) MsgDelegate {
//return MsgDelegate{
//DelegatorAddr: delegatorAddr,
//CandidateAddr: candidateAddr,
//Bond: sdk.Coin{"fermion", amt},
//}
//}
func newTestMsgDelegate(amt int64, delegatorAddr, candidateAddr sdk.Address) MsgDelegate {
return MsgDelegate{
DelegatorAddr: delegatorAddr,
CandidateAddr: candidateAddr,
Bond: sdk.Coin{"fermion", amt},
}
}
//func TestDuplicatesMsgDeclareCandidacy(t *testing.T) {
//ctxDeliver, _, keeper := createTestInput(t, addrs[0], false, 1000)
//ctxCheck, _, keeper := createTestInput(t, addrs[0], true, 1000)
func TestDuplicatesMsgDeclareCandidacy(t *testing.T) {
ctx, _, keeper := createTestInput(t, addrs[0], false, 1000)
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], 10)
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
//assert.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], 10)
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
assert.True(t, got.IsOK(), "%v", got)
//// one sender can bond to two different addresses
//msgDeclareCandidacy.Address = addrs[1]
//err := checker.declareCandidacy(msgDeclareCandidacy)
//assert.Nil(t, err, "didn't expected error on checkTx")
// one sender cannot bond twice
msgDeclareCandidacy.PubKey = pks[1]
got = handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
assert.False(t, got.IsOK(), "%v", got)
}
//// two addrs cant bond to the same pubkey
//checker.sender = addrs[1]
//msgDeclareCandidacy.Address = addrs[0]
//err = checker.declareCandidacy(msgDeclareCandidacy)
//assert.NotNil(t, err, "expected error on checkTx")
//}
func TestIncrementsMsgDelegate(t *testing.T) {
ctx, _, keeper := createTestInput(t, addrs[0], false, 1000)
//func TestIncrementsMsgDelegate(t *testing.T) {
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 1000)
// first declare candidacy
bondAmount := int64(10)
msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], bondAmount)
got := deliverer.declareCandidacy(msgDeclareCandidacy)
assert.NoError(t, got, "expected declare candidacy msg to be ok, got %v", got)
expectedBond := bondAmount // 1 since we send 1 at the start of loop,
//// first declare candidacy
//bondAmount := int64(10)
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], bondAmount)
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
//assert.NoError(t, got, "expected declare candidacy msg to be ok, got %v", got)
//expectedBond := bondAmount // 1 since we send 1 at the start of loop,
// just send the same msgbond multiple times
msgDelegate := newTestMsgDelegate(bondAmount, addrs[0])
for i := 0; i < 5; i++ {
got := deliverer.delegate(msgDelegate)
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//// just send the same msgbond multiple times
//msgDelegate := newTestMsgDelegate(bondAmount, addrs[0])
//for i := 0; i < 5; i++ {
//got := deliverer.delegate(msgDelegate)
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//Check that the accounts and the bond account have the appropriate values
candidates := mapper.GetCandidates()
expectedBond += bondAmount
//expectedSender := initSender - expectedBond
gotBonded := candidates[0].Liabilities.Evaluate()
//gotSender := accStore[string(deliverer.sender)] //XXX use StoreMapper
assert.Equal(t, expectedBond, gotBonded, "i: %v, %v, %v", i, expectedBond, gotBonded)
//assert.Equal(t, expectedSender, gotSender, "i: %v, %v, %v", i, expectedSender, gotSender) // XXX fix
}
}
////Check that the accounts and the bond account have the appropriate values
//candidates := mapper.GetCandidates()
//expectedBond += bondAmount
////expectedSender := initSender - expectedBond
//gotBonded := candidates[0].Liabilities.Evaluate()
////gotSender := accStore[string(deliverer.sender)] //XXX use StoreMapper
//assert.Equal(t, expectedBond, gotBonded, "i: %v, %v, %v", i, expectedBond, gotBonded)
////assert.Equal(t, expectedSender, gotSender, "i: %v, %v, %v", i, expectedSender, gotSender) // XXX fix
//}
//}
func TestIncrementsMsgUnbond(t *testing.T) {
ctx, _, keeper := createTestInput(t, addrs[0], false, 0)
//func TestIncrementsMsgUnbond(t *testing.T) {
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 0)
// set initial bond
initBond := int64(1000)
//accStore[string(deliverer.sender)] = initBond //XXX use StoreMapper
got := deliverer.declareCandidacy(newTestMsgDeclareCandidacy(addrs[0], pks[0], initBond))
assert.NoError(t, got, "expected initial bond msg to be ok, got %v", got)
//// set initial bond
//initBond := int64(1000)
////accStore[string(deliverer.sender)] = initBond //XXX use StoreMapper
//got := deliverer.declareCandidacy(newTestMsgDeclareCandidacy(addrs[0], pks[0], initBond))
//assert.NoError(t, got, "expected initial bond msg to be ok, got %v", got)
// just send the same msgunbond multiple times
// XXX use decimals here
unbondShares, unbondSharesStr := int64(10), "10"
msgUndelegate := NewMsgUnbond(addrs[0], unbondSharesStr)
nUnbonds := 5
for i := 0; i < nUnbonds; i++ {
got := deliverer.unbond(msgUndelegate)
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//// just send the same msgunbond multiple times
//// XXX use decimals here
//unbondShares, unbondSharesStr := int64(10), "10"
//msgUndelegate := NewMsgUnbond(addrs[0], unbondSharesStr)
//nUnbonds := 5
//for i := 0; i < nUnbonds; i++ {
//got := deliverer.unbond(msgUndelegate)
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//Check that the accounts and the bond account have the appropriate values
candidates := mapper.GetCandidates()
expectedBond := initBond - int64(i+1)*unbondShares // +1 since we send 1 at the start of loop
//expectedSender := initSender + (initBond - expectedBond)
gotBonded := candidates[0].Liabilities.Evaluate()
//gotSender := accStore[string(deliverer.sender)] // XXX use storemapper
////Check that the accounts and the bond account have the appropriate values
//candidates := mapper.GetCandidates()
//expectedBond := initBond - int64(i+1)*unbondShares // +1 since we send 1 at the start of loop
////expectedSender := initSender + (initBond - expectedBond)
//gotBonded := candidates[0].Liabilities.Evaluate()
////gotSender := accStore[string(deliverer.sender)] // XXX use storemapper
assert.Equal(t, expectedBond, gotBonded, "%v, %v", expectedBond, gotBonded)
//assert.Equal(t, expectedSender, gotSender, "%v, %v", expectedSender, gotSender) //XXX fix
}
//assert.Equal(t, expectedBond, gotBonded, "%v, %v", expectedBond, gotBonded)
////assert.Equal(t, expectedSender, gotSender, "%v, %v", expectedSender, gotSender) //XXX fix
//}
// these are more than we have bonded now
errorCases := []int64{
//1<<64 - 1, // more than int64
//1<<63 + 1, // more than int64
1<<63 - 1,
1 << 31,
initBond,
}
for _, c := range errorCases {
unbondShares := strconv.Itoa(int(c))
msgUndelegate := NewMsgUnbond(addrs[0], unbondShares)
got = deliverer.unbond(msgUndelegate)
assert.Error(t, got, "expected unbond msg to fail")
}
//// these are more than we have bonded now
//errorCases := []int64{
////1<<64 - 1, // more than int64
////1<<63 + 1, // more than int64
//1<<63 - 1,
//1 << 31,
//initBond,
//}
//for _, c := range errorCases {
//unbondShares := strconv.Itoa(int(c))
//msgUndelegate := NewMsgUnbond(addrs[0], unbondShares)
//got = deliverer.unbond(msgUndelegate)
//assert.Error(t, got, "expected unbond msg to fail")
//}
leftBonded := initBond - unbondShares*int64(nUnbonds)
//leftBonded := initBond - unbondShares*int64(nUnbonds)
// should be unable to unbond one more than we have
msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)+1))
got = deliverer.unbond(msgUndelegate)
assert.Error(t, got, "expected unbond msg to fail")
//// should be unable to unbond one more than we have
//msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)+1))
//got = deliverer.unbond(msgUndelegate)
//assert.Error(t, got, "expected unbond msg to fail")
// should be able to unbond just what we have
msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)))
got = deliverer.unbond(msgUndelegate)
assert.NoError(t, got, "expected unbond msg to pass")
}
//// should be able to unbond just what we have
//msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)))
//got = deliverer.unbond(msgUndelegate)
//assert.NoError(t, got, "expected unbond msg to pass")
//}
func TestMultipleMsgDeclareCandidacy(t *testing.T) {
initSender := int64(1000)
//ctx, accStore, mapper, deliverer := createTestInput(t, addrs[0], false, initSender)
ctx, mapper, keeper := createTestInput(t, addrs[0], false, initSender)
addrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
//func TestMultipleMsgDeclareCandidacy(t *testing.T) {
//initSender := int64(1000)
//ctx, accStore, mapper, deliverer := createTestInput(t, addrs[0], false, initSender)
//addrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
// bond them all
for i, addr := range addrs {
msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[i], pks[i], 10)
deliverer.sender = addr
got := deliverer.declareCandidacy(msgDeclareCandidacy)
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//// bond them all
//for i, addr := range addrs {
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[i], pks[i], 10)
//deliverer.sender = addr
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//Check that the account is bonded
candidates := mapper.GetCandidates()
require.Equal(t, i, len(candidates))
val := candidates[i]
balanceExpd := initSender - 10
balanceGot := accStore.GetAccount(ctx, val.Address).GetCoins()
assert.Equal(t, i+1, len(candidates), "expected %d candidates got %d, candidates: %v", i+1, len(candidates), candidates)
assert.Equal(t, 10, int(val.Liabilities.Evaluate()), "expected %d shares, got %d", 10, val.Liabilities)
assert.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
}
////Check that the account is bonded
//candidates := mapper.GetCandidates()
//require.Equal(t, i, len(candidates))
//val := candidates[i]
//balanceExpd := initSender - 10
//balanceGot := accStore.GetAccount(ctx, val.Address).GetCoins()
//assert.Equal(t, i+1, len(candidates), "expected %d candidates got %d, candidates: %v", i+1, len(candidates), candidates)
//assert.Equal(t, 10, int(val.Liabilities.Evaluate()), "expected %d shares, got %d", 10, val.Liabilities)
//assert.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
//}
// unbond them all
for i, addr := range addrs {
candidatePre := mapper.GetCandidate(addrs[i])
msgUndelegate := NewMsgUnbond(addrs[i], "10")
deliverer.sender = addr
got := deliverer.unbond(msgUndelegate)
assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//// unbond them all
//for i, addr := range addrs {
//candidatePre := mapper.GetCandidate(addrs[i])
//msgUndelegate := NewMsgUnbond(addrs[i], "10")
//deliverer.sender = addr
//got := deliverer.unbond(msgUndelegate)
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//Check that the account is unbonded
candidates := mapper.GetCandidates()
assert.Equal(t, len(addrs)-(i+1), len(candidates), "expected %d candidates got %d", len(addrs)-(i+1), len(candidates))
////Check that the account is unbonded
//candidates := mapper.GetCandidates()
//assert.Equal(t, len(addrs)-(i+1), len(candidates), "expected %d candidates got %d", len(addrs)-(i+1), len(candidates))
candidatePost := mapper.GetCandidate(addrs[i])
balanceExpd := initSender
balanceGot := accStore.GetAccount(ctx, candidatePre.Address).GetCoins()
assert.Nil(t, candidatePost, "expected nil candidate retrieve, got %d", 0, candidatePost)
assert.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
}
}
//candidatePost := mapper.GetCandidate(addrs[i])
//balanceExpd := initSender
//balanceGot := accStore.GetAccount(ctx, candidatePre.Address).GetCoins()
//assert.Nil(t, candidatePost, "expected nil candidate retrieve, got %d", 0, candidatePost)
//assert.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
//}
//}
func TestMultipleMsgDelegate(t *testing.T) {
sender, delegators := addrs[0], addrs[1:]
_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 1000)
ctx, _, keeper := createTestInput(t, addrs[0], false, 0)
//func TestMultipleMsgDelegate(t *testing.T) {
//sender, delegators := addrs[0], addrs[1:]
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 1000)
//first make a candidate
msgDeclareCandidacy := newTestMsgDeclareCandidacy(sender, pks[0], 10)
got := deliverer.declareCandidacy(msgDeclareCandidacy)
require.NoError(t, got, "expected msg to be ok, got %v", got)
////first make a candidate
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(sender, pks[0], 10)
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
//require.NoError(t, got, "expected msg to be ok, got %v", got)
// delegate multiple parties
for i, delegator := range delegators {
msgDelegate := newTestMsgDelegate(10, sender)
deliverer.sender = delegator
got := deliverer.delegate(msgDelegate)
require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//// delegate multiple parties
//for i, delegator := range delegators {
//msgDelegate := newTestMsgDelegate(10, sender)
//deliverer.sender = delegator
//got := deliverer.delegate(msgDelegate)
//require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//Check that the account is bonded
bond := mapper.getDelegatorBond(delegator, sender)
assert.NotNil(t, bond, "expected delegatee bond %d to exist", bond)
}
////Check that the account is bonded
//bond := mapper.getDelegatorBond(delegator, sender)
//assert.NotNil(t, bond, "expected delegatee bond %d to exist", bond)
//}
// unbond them all
for i, delegator := range delegators {
msgUndelegate := NewMsgUnbond(sender, "10")
deliverer.sender = delegator
got := deliverer.unbond(msgUndelegate)
require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//// unbond them all
//for i, delegator := range delegators {
//msgUndelegate := NewMsgUnbond(sender, "10")
//deliverer.sender = delegator
//got := deliverer.unbond(msgUndelegate)
//require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
//Check that the account is unbonded
bond := mapper.getDelegatorBond(delegator, sender)
assert.Nil(t, bond, "expected delegatee bond %d to be nil", bond)
}
}
////Check that the account is unbonded
//bond := mapper.getDelegatorBond(delegator, sender)
//assert.Nil(t, bond, "expected delegatee bond %d to be nil", bond)
//}
//}
func TestVoidCandidacy(t *testing.T) {
sender, delegator := addrs[0], addrs[1]
_, _, _, deliverer := createTestInput(t, addrs[0], false, 1000)
//func TestVoidCandidacy(t *testing.T) {
//sender, delegator := addrs[0], addrs[1]
//_, _, _, deliverer := createTestInput(t, addrs[0], false, 1000)
// create the candidate
msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], 10)
got := deliverer.declareCandidacy(msgDeclareCandidacy)
require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
//// create the candidate
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], 10)
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
// bond a delegator
msgDelegate := newTestMsgDelegate(10, addrs[0])
deliverer.sender = delegator
got = deliverer.delegate(msgDelegate)
require.NoError(t, got, "expected ok, got %v", got)
//// bond a delegator
//msgDelegate := newTestMsgDelegate(10, addrs[0])
//deliverer.sender = delegator
//got = deliverer.delegate(msgDelegate)
//require.NoError(t, got, "expected ok, got %v", got)
// unbond the candidates bond portion
msgUndelegate := NewMsgUnbond(addrs[0], "10")
deliverer.sender = sender
got = deliverer.unbond(msgUndelegate)
require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
//// unbond the candidates bond portion
//msgUndelegate := NewMsgUnbond(addrs[0], "10")
//deliverer.sender = sender
//got = deliverer.unbond(msgUndelegate)
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
// test that this pubkey cannot yet be bonded too
deliverer.sender = delegator
got = deliverer.delegate(msgDelegate)
assert.Error(t, got, "expected error, got %v", got)
//// test that this pubkey cannot yet be bonded too
//deliverer.sender = delegator
//got = deliverer.delegate(msgDelegate)
//assert.Error(t, got, "expected error, got %v", got)
// test that the delegator can still withdraw their bonds
got = deliverer.unbond(msgUndelegate)
require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
//// test that the delegator can still withdraw their bonds
//got = deliverer.unbond(msgUndelegate)
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
//// verify that the pubkey can now be reused
//got = deliverer.declareCandidacy(msgDeclareCandidacy)
//assert.NoError(t, got, "expected ok, got %v", got)
//}
// verify that the pubkey can now be reused
got = deliverer.declareCandidacy(msgDeclareCandidacy)
assert.NoError(t, got, "expected ok, got %v", got)
}
*/

View File

@ -362,3 +362,33 @@ func (k Keeper) setParams(ctx sdk.Context, params Params) {
store.Set(ParamKey, b)
k.params = Params{} // clear the cache
}
//_______________________________________________________________________
// load/save the pool
func (k Keeper) GetPool(ctx sdk.Context) (gs Pool) {
// check if cached before anything
if k.gs != (Pool{}) {
return k.gs
}
store := ctx.KVStore(k.storeKey)
b := store.Get(PoolKey)
if b == nil {
return initialPool()
}
err := k.cdc.UnmarshalBinary(b, &gs)
if err != nil {
panic(err) // This error should never occur big problem if does
}
return
}
func (k Keeper) setPool(ctx sdk.Context, p Pool) {
store := ctx.KVStore(k.storeKey)
b, err := k.cdc.MarshalBinary(p)
if err != nil {
panic(err)
}
store.Set(PoolKey, b)
k.gs = Pool{} // clear the cache
}

View File

@ -16,6 +16,7 @@ var (
ValidatorsKey = []byte{0x03} // prefix for each key to a validator
AccUpdateValidatorsKey = []byte{0x04} // prefix for each key to a validator which is being updated
RecentValidatorsKey = []byte{0x05} // prefix for each key to the last updated validator group
ToKickOutValidatorsKey = []byte{0x06} // prefix for each key to the last updated validator group
DelegatorBondKeyPrefix = []byte{0x07} // prefix for each key to a delegator's bond

View File

@ -558,3 +558,18 @@ func TestParams(t *testing.T) {
resParams = keeper.GetParams(ctx)
assert.Equal(t, expParams, resParams)
}
func TestPool(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
expPool := initialPool()
//check that the empty keeper loads the default
resPool := keeper.GetPool(ctx)
assert.Equal(t, expPool, resPool)
//modify a params, save, and retrieve
expPool.TotalSupply = 777
keeper.setPool(ctx, expPool)
resPool = keeper.GetPool(ctx)
assert.Equal(t, expPool, resPool)
}

View File

@ -4,132 +4,115 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// load/save the global staking state
func (k Keeper) GetPool(ctx sdk.Context) (gs Pool) {
// check if cached before anything
if k.gs != (Pool{}) {
return k.gs
// get the bond ratio of the global state
func (p Pool) bondedRatio() sdk.Rat {
if p.TotalSupply > 0 {
return sdk.NewRat(p.BondedPool, p.TotalSupply)
}
store := ctx.KVStore(k.storeKey)
b := store.Get(PoolKey)
if b == nil {
return initialPool()
}
err := k.cdc.UnmarshalBinary(b, &gs)
if err != nil {
panic(err) // This error should never occur big problem if does
}
return
return sdk.ZeroRat
}
func (k Keeper) setPool(ctx sdk.Context, p Pool) {
store := ctx.KVStore(k.storeKey)
b, err := k.cdc.MarshalBinary(p)
if err != nil {
panic(err)
// get the exchange rate of bonded token per issued share
func (p Pool) bondedShareExRate() sdk.Rat {
if p.BondedShares.IsZero() {
return sdk.OneRat
}
store.Set(PoolKey, b)
k.gs = Pool{} // clear the cache
return sdk.NewRat(p.BondedPool).Quo(p.BondedShares)
}
//_______________________________________________________________________
//TODO make these next two functions more efficient should be reading and writting to state ye know
// get the exchange rate of unbonded tokens held in candidates per issued share
func (p Pool) unbondedShareExRate() sdk.Rat {
if p.UnbondedShares.IsZero() {
return sdk.OneRat
}
return sdk.NewRat(p.UnbondedPool).Quo(p.UnbondedShares)
}
// move a candidates asset pool from bonded to unbonded pool
func (k Keeper) bondedToUnbondedPool(ctx sdk.Context, candidate Candidate) {
func (p Pool) bondedToUnbondedPool(candidate Candidate) (Pool, Candidate) {
// replace bonded shares with unbonded shares
tokens := k.removeSharesBonded(ctx, candidate.Assets)
candidate.Assets = k.addTokensUnbonded(ctx, tokens)
p, tokens := p.removeSharesBonded(candidate.Assets)
p, candidate.Assets = p.addTokensUnbonded(tokens)
candidate.Status = Unbonded
k.setCandidate(ctx, candidate)
return p, candidate
}
// move a candidates asset pool from unbonded to bonded pool
func (k Keeper) unbondedToBondedPool(ctx sdk.Context, candidate Candidate) {
func (p Pool) unbondedToBondedPool(candidate Candidate) (Pool, Candidate) {
// replace unbonded shares with bonded shares
tokens := k.removeSharesUnbonded(ctx, candidate.Assets)
candidate.Assets = k.addTokensBonded(ctx, tokens)
p, tokens := p.removeSharesUnbonded(candidate.Assets)
p, candidate.Assets = p.addTokensBonded(tokens)
candidate.Status = Bonded
k.setCandidate(ctx, candidate)
return p, candidate
}
//_______________________________________________________________________
func (k Keeper) addTokensBonded(ctx sdk.Context, amount int64) (issuedShares sdk.Rat) {
p := k.GetPool(ctx)
issuedShares = p.bondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
func (p Pool) addTokensBonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
issuedShares = sdk.NewRat(amount).Quo(p.bondedShareExRate()) // (tokens/shares)^-1 * tokens
p.BondedPool += amount
p.BondedShares = p.BondedShares.Add(issuedShares)
k.setPool(ctx, p)
return
return p, issuedShares
}
func (k Keeper) removeSharesBonded(ctx sdk.Context, shares sdk.Rat) (removedTokens int64) {
p := k.GetPool(ctx)
func (p Pool) removeSharesBonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
removedTokens = p.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
p.BondedShares = p.BondedShares.Sub(shares)
p.BondedPool -= removedTokens
k.setPool(ctx, p)
return
return p, removedTokens
}
func (k Keeper) addTokensUnbonded(ctx sdk.Context, amount int64) (issuedShares sdk.Rat) {
p := k.GetPool(ctx)
func (p Pool) addTokensUnbonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
issuedShares = p.unbondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
p.UnbondedShares = p.UnbondedShares.Add(issuedShares)
p.UnbondedPool += amount
k.setPool(ctx, p)
return
return p, issuedShares
}
func (k Keeper) removeSharesUnbonded(ctx sdk.Context, shares sdk.Rat) (removedTokens int64) {
p := k.GetPool(ctx)
func (p Pool) removeSharesUnbonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
removedTokens = p.unbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
p.UnbondedShares = p.UnbondedShares.Sub(shares)
p.UnbondedPool -= removedTokens
k.setPool(ctx, p)
return
return p, removedTokens
}
//_______________________________________________________________________
// add tokens to a candidate
func (k Keeper) candidateAddTokens(ctx sdk.Context, candidate Candidate, amount int64) (issuedDelegatorShares sdk.Rat) {
func (p Pool) candidateAddTokens(candidate Candidate,
amount int64) (p2 Pool, candidate2 Candidate, issuedDelegatorShares sdk.Rat) {
p := k.GetPool(ctx)
exRate := candidate.delegatorShareExRate()
var receivedGlobalShares sdk.Rat
if candidate.Status == Bonded {
receivedGlobalShares = k.addTokensBonded(ctx, amount)
p, receivedGlobalShares = p.addTokensBonded(amount)
} else {
receivedGlobalShares = k.addTokensUnbonded(ctx, amount)
p, receivedGlobalShares = p.addTokensUnbonded(amount)
}
candidate.Assets = candidate.Assets.Add(receivedGlobalShares)
issuedDelegatorShares = exRate.Mul(receivedGlobalShares)
candidate.Liabilities = candidate.Liabilities.Add(issuedDelegatorShares)
k.setPool(ctx, p) // TODO cache Pool?
return
return p, candidate, issuedDelegatorShares
}
// remove shares from a candidate
func (k Keeper) candidateRemoveShares(ctx sdk.Context, candidate Candidate, shares sdk.Rat) (createdCoins int64) {
func (p Pool) candidateRemoveShares(candidate Candidate,
shares sdk.Rat) (p2 Pool, candidate2 Candidate, createdCoins int64) {
p := k.GetPool(ctx)
//exRate := candidate.delegatorShareExRate() //XXX make sure not used
globalPoolSharesToRemove := candidate.delegatorShareExRate().Mul(shares)
if candidate.Status == Bonded {
createdCoins = k.removeSharesBonded(ctx, globalPoolSharesToRemove)
p, createdCoins = p.removeSharesBonded(globalPoolSharesToRemove)
} else {
createdCoins = k.removeSharesUnbonded(ctx, globalPoolSharesToRemove)
p, createdCoins = p.removeSharesUnbonded(globalPoolSharesToRemove)
}
candidate.Assets = candidate.Assets.Sub(globalPoolSharesToRemove)
candidate.Liabilities = candidate.Liabilities.Sub(shares)
k.setPool(ctx, p) // TODO cache Pool?
return
return p, candidate, createdCoins
}

View File

@ -1,22 +1,374 @@
package stake
import (
"fmt"
"math/rand"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestPool(t *testing.T) {
func TestBondedToUnbondedPool(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
expPool := initialPool()
//check that the empty keeper loads the default
resPool := keeper.GetPool(ctx)
assert.Equal(t, expPool, resPool)
poolA := keeper.GetPool(ctx)
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
candA := Candidate{
Status: Bonded,
Address: addrs[0],
PubKey: pks[0],
Assets: sdk.OneRat,
Liabilities: sdk.OneRat,
}
poolB, candB := poolA.bondedToUnbondedPool(candA)
//modify a params, save, and retrieve
expPool.TotalSupply = 777
keeper.setPool(ctx, expPool)
resPool = keeper.GetPool(ctx)
assert.Equal(t, expPool, resPool)
// status unbonded
assert.Equal(t, candB.Status, Unbonded)
// same exchange rate, assets unchanged
assert.Equal(t, candB.Assets, candA.Assets)
// bonded pool decreased
assert.Equal(t, poolB.BondedPool, poolA.BondedPool-candA.Assets.Evaluate())
// unbonded pool increased
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool+candA.Assets.Evaluate())
// conservation of tokens
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool, poolA.BondedPool+poolA.UnbondedPool)
}
func TestUnbonbedtoBondedPool(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
poolA := keeper.GetPool(ctx)
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
candA := Candidate{
Status: Bonded,
Address: addrs[0],
PubKey: pks[0],
Assets: sdk.OneRat,
Liabilities: sdk.OneRat,
}
candA.Status = Unbonded
poolB, candB := poolA.unbondedToBondedPool(candA)
// status bonded
assert.Equal(t, candB.Status, Bonded)
// same exchange rate, assets unchanged
assert.Equal(t, candB.Assets, candA.Assets)
// bonded pool increased
assert.Equal(t, poolB.BondedPool, poolA.BondedPool+candA.Assets.Evaluate())
// unbonded pool decreased
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool-candA.Assets.Evaluate())
// conservation of tokens
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool, poolA.BondedPool+poolA.UnbondedPool)
}
func TestAddTokensBonded(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
poolA := keeper.GetPool(ctx)
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
poolB, sharesB := poolA.addTokensBonded(10)
assert.Equal(t, poolB.bondedShareExRate(), sdk.OneRat)
// correct changes to bonded shares and bonded pool
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Add(sharesB))
assert.Equal(t, poolB.BondedPool, poolA.BondedPool+10)
// same number of bonded shares / tokens when exchange rate is one
assert.Equal(t, poolB.BondedShares, sdk.NewRat(poolB.BondedPool))
}
func TestRemoveSharesBonded(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
poolA := keeper.GetPool(ctx)
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
poolB, tokensB := poolA.removeSharesBonded(sdk.NewRat(10))
assert.Equal(t, poolB.bondedShareExRate(), sdk.OneRat)
// correct changes to bonded shares and bonded pool
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Sub(sdk.NewRat(10)))
assert.Equal(t, poolB.BondedPool, poolA.BondedPool-tokensB)
// same number of bonded shares / tokens when exchange rate is one
assert.Equal(t, poolB.BondedShares, sdk.NewRat(poolB.BondedPool))
}
func TestAddTokensUnbonded(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
poolA := keeper.GetPool(ctx)
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
poolB, sharesB := poolA.addTokensUnbonded(10)
assert.Equal(t, poolB.unbondedShareExRate(), sdk.OneRat)
// correct changes to unbonded shares and unbonded pool
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Add(sharesB))
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool+10)
// same number of unbonded shares / tokens when exchange rate is one
assert.Equal(t, poolB.UnbondedShares, sdk.NewRat(poolB.UnbondedPool))
}
func TestRemoveSharesUnbonded(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
poolA := keeper.GetPool(ctx)
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
poolB, tokensB := poolA.removeSharesUnbonded(sdk.NewRat(10))
assert.Equal(t, poolB.unbondedShareExRate(), sdk.OneRat)
// correct changes to unbonded shares and bonded pool
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Sub(sdk.NewRat(10)))
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool-tokensB)
// same number of unbonded shares / tokens when exchange rate is one
assert.Equal(t, poolB.UnbondedShares, sdk.NewRat(poolB.UnbondedPool))
}
func TestCandidateAddTokens(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
poolA := keeper.GetPool(ctx)
candA := Candidate{
Status: Bonded,
Address: addrs[0],
PubKey: pks[0],
Assets: sdk.NewRat(9),
Liabilities: sdk.NewRat(9),
}
poolA.BondedPool = candA.Assets.Evaluate()
poolA.BondedShares = candA.Assets
assert.Equal(t, candA.delegatorShareExRate(), sdk.OneRat)
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
poolB, candB, sharesB := poolA.candidateAddTokens(candA, 10)
// shares were issued
assert.Equal(t, sdk.NewRat(10).Mul(candA.delegatorShareExRate()), sharesB)
// pool shares were added
assert.Equal(t, candB.Assets, candA.Assets.Add(sdk.NewRat(10)))
// conservation of tokens
assert.Equal(t, poolB.BondedPool, 10+poolA.BondedPool)
}
func TestCandidateRemoveShares(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
poolA := keeper.GetPool(ctx)
candA := Candidate{
Status: Bonded,
Address: addrs[0],
PubKey: pks[0],
Assets: sdk.NewRat(9),
Liabilities: sdk.NewRat(9),
}
poolA.BondedPool = candA.Assets.Evaluate()
poolA.BondedShares = candA.Assets
assert.Equal(t, candA.delegatorShareExRate(), sdk.OneRat)
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
poolB, candB, coinsB := poolA.candidateRemoveShares(candA, sdk.NewRat(10))
// coins were created
assert.Equal(t, coinsB, int64(10))
// pool shares were removed
assert.Equal(t, candB.Assets, candA.Assets.Sub(sdk.NewRat(10).Mul(candA.delegatorShareExRate())))
// conservation of tokens
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool+coinsB, poolA.UnbondedPool+poolA.BondedPool)
}
/////////////////////////////////////
// TODO Make all random tests less obfuscated!
// generate a random candidate
func randomCandidate(r *rand.Rand) Candidate {
var status CandidateStatus
if r.Float64() < float64(0.5) {
status = Bonded
} else {
status = Unbonded
}
assets := sdk.NewRat(int64(r.Int31n(10000)))
liabilities := sdk.NewRat(int64(r.Int31n(10000)))
return Candidate{
Status: status,
Address: addrs[0],
PubKey: pks[0],
Assets: assets,
Liabilities: liabilities,
}
}
// generate a random staking state
func randomSetup(r *rand.Rand) (Pool, Candidate) {
pool := Pool{
TotalSupply: 0,
BondedShares: sdk.ZeroRat,
UnbondedShares: sdk.ZeroRat,
BondedPool: 0,
UnbondedPool: 0,
InflationLastTime: 0,
Inflation: sdk.NewRat(7, 100),
}
candidate := randomCandidate(r)
if candidate.Status == Bonded {
pool.BondedShares = pool.BondedShares.Add(candidate.Assets)
pool.BondedPool += candidate.Assets.Evaluate()
} else {
pool.UnbondedShares = pool.UnbondedShares.Add(candidate.Assets)
pool.UnbondedPool += candidate.Assets.Evaluate()
}
return pool, candidate
}
func randomTokens(r *rand.Rand) int64 {
return int64(r.Int31n(10000))
}
// operation that transforms staking state
type Operation func(p Pool, c Candidate) (Pool, Candidate, int64, string)
// pick a random staking operation
func randomOperation(r *rand.Rand) Operation {
operations := []Operation{
// bond/unbond
func(p Pool, cand Candidate) (Pool, Candidate, int64, string) {
var msg string
if cand.Status == Bonded {
msg = fmt.Sprintf("Unbonded previously bonded candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
p, cand = p.bondedToUnbondedPool(cand)
} else {
msg = fmt.Sprintf("Bonded previously unbonded candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
p, cand = p.unbondedToBondedPool(cand)
}
return p, cand, 0, msg
},
// add some tokens to a candidate
func(p Pool, cand Candidate) (Pool, Candidate, int64, string) {
tokens := int64(r.Int31n(1000))
msg := fmt.Sprintf("candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
p, cand, _ = p.candidateAddTokens(cand, tokens)
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
return p, cand, -1 * tokens, msg // tokens are removed so for accounting must be negative
},
// remove some shares from a candidate
func(p Pool, cand Candidate) (Pool, Candidate, int64, string) {
shares := sdk.NewRat(int64(r.Int31n(1000)))
if shares.GT(cand.Liabilities) {
shares = cand.Liabilities.Quo(sdk.NewRat(2))
}
msg := fmt.Sprintf("candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
p, cand, tokens := p.candidateRemoveShares(cand, shares)
msg = fmt.Sprintf("Removed %d shares from %s", shares.Evaluate(), msg)
return p, cand, tokens, msg
},
}
r.Shuffle(len(operations), func(i, j int) {
operations[i], operations[j] = operations[j], operations[i]
})
return operations[0]
}
// ensure invariants that should always be true are true
func assertInvariants(t *testing.T, msg string,
pOrig Pool, cOrig Candidate, pMod Pool, cMod Candidate, tokens int64) {
// total tokens conserved
require.Equal(t,
pOrig.UnbondedPool+pOrig.BondedPool,
pMod.UnbondedPool+pMod.BondedPool+tokens,
"msg: %v\n, pOrig.UnbondedPool: %v, pOrig.BondedPool: %v, pMod.UnbondedPool: %v, pMod.BondedPool: %v, tokens: %v\n",
msg,
pOrig.UnbondedPool, pOrig.BondedPool,
pMod.UnbondedPool, pMod.BondedPool, tokens)
// nonnegative shares
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat),
"msg: %v\n, pOrig: %v\n, pMod: %v\n, cOrig: %v\n, cMod %v, tokens: %v\n",
msg, pOrig, pMod, cOrig, cMod, tokens)
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat),
"msg: %v\n, pOrig: %v\n, pMod: %v\n, cOrig: %v\n, cMod %v, tokens: %v\n",
msg, pOrig, pMod, cOrig, cMod, tokens)
// nonnegative ex rates
require.False(t, pMod.bondedShareExRate().LT(sdk.ZeroRat),
"Applying operation \"%s\" resulted in negative bondedShareExRate: %d",
msg, pMod.bondedShareExRate().Evaluate())
require.False(t, pMod.unbondedShareExRate().LT(sdk.ZeroRat),
"Applying operation \"%s\" resulted in negative unbondedShareExRate: %d",
msg, pMod.unbondedShareExRate().Evaluate())
// nonnegative ex rate
require.False(t, cMod.delegatorShareExRate().LT(sdk.ZeroRat),
"Applying operation \"%s\" resulted in negative candidate.delegatorShareExRate(): %v (candidate.PubKey: %s)",
msg,
cMod.delegatorShareExRate(),
cMod.PubKey,
)
// nonnegative assets / liabilities
require.False(t, cMod.Assets.LT(sdk.ZeroRat),
"Applying operation \"%s\" resulted in negative candidate.Assets: %d (candidate.Liabilities: %d, candidate.PubKey: %s)",
msg,
cMod.Assets.Evaluate(),
cMod.Liabilities.Evaluate(),
cMod.PubKey,
)
require.False(t, cMod.Liabilities.LT(sdk.ZeroRat),
"Applying operation \"%s\" resulted in negative candidate.Liabilities: %d (candidate.Assets: %d, candidate.PubKey: %s)",
msg,
cMod.Liabilities.Evaluate(),
cMod.Assets.Evaluate(),
cMod.PubKey,
)
}
// run random operations in a random order on a random state, assert invariants hold
func TestIntegrationInvariants(t *testing.T) {
for i := 0; i < 10; i++ {
r1 := rand.New(rand.NewSource(time.Now().UnixNano()))
pool, candidates := randomSetup(r1)
initialPool, initialCandidates := pool, candidates
assertInvariants(t, "no operation",
initialPool, initialCandidates,
pool, candidates, 0)
for j := 0; j < 100; j++ {
r2 := rand.New(rand.NewSource(time.Now().UnixNano()))
pool, candidates, tokens, msg := randomOperation(r2)(pool, candidates)
assertInvariants(t, msg,
initialPool, initialCandidates,
pool, candidates, tokens)
}
}
}

View File

@ -124,12 +124,11 @@ func createTestInput(t *testing.T, sender sdk.Address, isCheckTx bool, initCoins
ck := bank.NewCoinKeeper(accountMapper)
keeper := NewKeeper(ctx, cdc, keyStake, ck)
//params := paramsNoInflation()
params := keeper.GetParams(ctx)
// fill all the addresses with some coins
for _, addr := range addrs {
ck.AddCoins(ctx, addr, sdk.Coins{{params.BondDenom, initCoins}})
ck.AddCoins(ctx, addr, sdk.Coins{
{keeper.GetParams(ctx).BondDenom, initCoins},
})
}
return ctx, accountMapper, keeper

View File

@ -2,42 +2,38 @@ package stake
import (
sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/abci/types"
)
const (
hrsPerYear = 8766 // as defined by a julian year of 365.25 days
precision = 1000000000
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
precision = 1000000000
)
var hrsPerYrRat = sdk.NewRat(hrsPerYear) // as defined by a julian year of 365.25 days
var hrsPerYrRat = sdk.NewRat(hrsPerYr) // as defined by a julian year of 365.25 days
// Tick - called at the end of every block
func (k Keeper) Tick(ctx sdk.Context) (change []*abci.Validator, err error) {
// retrieve params
func (k Keeper) Tick(ctx sdk.Context) (change []Validator) {
p := k.GetPool(ctx)
height := ctx.BlockHeight()
// Process Validator Provisions
// XXX right now just process every 5 blocks, in new SDK make hourly
if p.InflationLastTime+5 <= height {
p.InflationLastTime = height
k.processProvisions(ctx)
blockTime := ctx.BlockHeader().Time // XXX assuming in seconds, confirm
if p.InflationLastTime+blockTime >= 3600 {
p.InflationLastTime = blockTime
p = k.processProvisions(ctx)
}
newVals := k.GetValidators(ctx)
// save the params
k.setPool(ctx, p)
// XXX determine change from old validators, set to change
_ = newVals
return change, nil
change = k.getAccUpdateValidators(ctx)
return
}
// process provisions for an hour period
func (k Keeper) processProvisions(ctx sdk.Context) {
func (k Keeper) processProvisions(ctx sdk.Context) Pool {
pool := k.GetPool(ctx)
pool.Inflation = k.nextInflation(ctx).Round(precision)
pool.Inflation = k.nextInflation(ctx)
// Because the validators hold a relative bonded share (`GlobalStakeShare`), when
// more bonded tokens are added proportionally to all validators the only term
@ -46,9 +42,7 @@ func (k Keeper) processProvisions(ctx sdk.Context) {
provisions := pool.Inflation.Mul(sdk.NewRat(pool.TotalSupply)).Quo(hrsPerYrRat).Evaluate()
pool.BondedPool += provisions
pool.TotalSupply += provisions
// save the params
k.setPool(ctx, pool)
return pool
}
// get the next inflation rate for the hour
@ -75,5 +69,5 @@ func (k Keeper) nextInflation(ctx sdk.Context) (inflation sdk.Rat) {
inflation = params.InflationMin
}
return
return inflation.Round(precision)
}

View File

@ -1,116 +1,134 @@
package stake
//import (
//"testing"
import (
"testing"
//sdk "github.com/cosmos/cosmos-sdk/types"
//"github.com/stretchr/testify/assert"
//)
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
//func TestGetInflation(t *testing.T) {
//ctx, _, keeper := createTestInput(t, nil, false, 0)
//params := defaultParams()
//keeper.setParams(ctx, params)
//gs := keeper.GetPool(ctx)
func TestGetInflation(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
pool := keeper.GetPool(ctx)
params := keeper.GetParams(ctx)
hrsPerYrRat := sdk.NewRat(hrsPerYr)
//// Governing Mechanism:
//// bondedRatio = BondedPool / TotalSupply
//// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
// Governing Mechanism:
// bondedRatio = BondedPool / TotalSupply
// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
//tests := []struct {
//setBondedPool, setTotalSupply int64
//setInflation, expectedChange sdk.Rat
//}{
//// with 0% bonded atom supply the inflation should increase by InflationRateChange
//{0, 0, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYr)},
tests := []struct {
name string
setBondedPool, setTotalSupply int64
setInflation, expectedChange sdk.Rat
}{
// with 0% bonded atom supply the inflation should increase by InflationRateChange
{"test 1", 0, 0, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYrRat).Round(precision)},
//// 100% bonded, starting at 20% inflation and being reduced
//{1, 1, sdk.NewRat(20, 100), sdk.OneRat.Sub(sdk.OneRat.Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYr)},
// 100% bonded, starting at 20% inflation and being reduced
// (1 - (1/0.67))*(0.13/8667)
{"test 2", 1, 1, sdk.NewRat(20, 100),
sdk.OneRat.Sub(sdk.OneRat.Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
//// 50% bonded, starting at 10% inflation and being increased
//{1, 2, sdk.NewRat(10, 100), sdk.OneRat.Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYr)},
// 50% bonded, starting at 10% inflation and being increased
{"test 3", 1, 2, sdk.NewRat(10, 100),
sdk.OneRat.Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
//// test 7% minimum stop (testing with 100% bonded)
//{1, 1, sdk.NewRat(7, 100), sdk.ZeroRat},
//{1, 1, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000)},
// test 7% minimum stop (testing with 100% bonded)
{"test 4", 1, 1, sdk.NewRat(7, 100), sdk.ZeroRat},
{"test 5", 1, 1, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
//// test 20% maximum stop (testing with 0% bonded)
//{0, 0, sdk.NewRat(20, 100), sdk.ZeroRat},
//{0, 0, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000)},
// test 20% maximum stop (testing with 0% bonded)
{"test 6", 0, 0, sdk.NewRat(20, 100), sdk.ZeroRat},
{"test 7", 0, 0, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000).Round(precision)},
//// perfect balance shouldn't change inflation
//{67, 100, sdk.NewRat(15, 100), sdk.ZeroRat},
//}
//for _, tc := range tests {
//gs.BondedPool, p.TotalSupply = tc.setBondedPool, tc.setTotalSupply
//gs.Inflation = tc.setInflation
// perfect balance shouldn't change inflation
{"test 8", 67, 100, sdk.NewRat(15, 100), sdk.ZeroRat},
}
for _, tc := range tests {
pool.BondedPool, pool.TotalSupply = tc.setBondedPool, tc.setTotalSupply
pool.Inflation = tc.setInflation
keeper.setPool(ctx, pool)
//inflation := nextInflation(gs, params)
//diffInflation := inflation.Sub(tc.setInflation)
inflation := keeper.nextInflation(ctx)
diffInflation := inflation.Sub(tc.setInflation)
//assert.True(t, diffInflation.Equal(tc.expectedChange),
//"%v, %v", diffInflation, tc.expectedChange)
//}
//}
assert.True(t, diffInflation.Equal(tc.expectedChange),
"Name: %v\nDiff: %v\nExpected: %v\n", tc.name, diffInflation, tc.expectedChange)
}
}
//func TestProcessProvisions(t *testing.T) {
//ctx, _, keeper := createTestInput(t, nil, false, 0)
//params := defaultParams()
//keeper.setParams(ctx, params)
//gs := keeper.GetPool(ctx)
func TestProcessProvisions(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
params := defaultParams()
keeper.setParams(ctx, params)
pool := keeper.GetPool(ctx)
//// create some candidates some bonded, some unbonded
//candidates := candidatesFromAddrsEmpty(addrs)
//for i, candidate := range candidates {
//if i < 5 {
//candidate.Status = Bonded
//}
//mintedTokens := int64((i + 1) * 10000000)
//gs.TotalSupply += mintedTokens
//keeper.candidateAddTokens(ctx, candidate, mintedTokens)
//keeper.setCandidate(ctx, candidate)
//}
//var totalSupply int64 = 550000000
//var bondedShares int64 = 150000000
//var unbondedShares int64 = 400000000
// create some candidates some bonded, some unbonded
candidates := make([]Candidate, 10)
for i := 0; i < 10; i++ {
c := Candidate{
Status: Unbonded,
PubKey: pks[i],
Address: addrs[i],
Assets: sdk.NewRat(0),
Liabilities: sdk.NewRat(0),
}
if i < 5 {
c.Status = Bonded
}
mintedTokens := int64((i + 1) * 10000000)
pool.TotalSupply += mintedTokens
pool, c, _ = pool.candidateAddTokens(c, mintedTokens)
//// initial bonded ratio ~ 27%
//assert.True(t, p.bondedRatio().Equal(sdk.NewRat(bondedShares, totalSupply)), "%v", p.bondedRatio())
keeper.setCandidate(ctx, c)
candidates[i] = c
}
keeper.setPool(ctx, pool)
var totalSupply int64 = 550000000
var bondedShares int64 = 150000000
var unbondedShares int64 = 400000000
assert.Equal(t, totalSupply, pool.TotalSupply)
assert.Equal(t, bondedShares, pool.BondedPool)
assert.Equal(t, unbondedShares, pool.UnbondedPool)
//// Supplies
//assert.Equal(t, totalSupply, p.TotalSupply)
//assert.Equal(t, bondedShares, p.BondedPool)
//assert.Equal(t, unbondedShares, p.UnbondedPool)
// initial bonded ratio ~ 27%
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(bondedShares, totalSupply)), "%v", pool.bondedRatio())
//// test the value of candidate shares
//assert.True(t, p.bondedShareExRate().Equal(sdk.OneRat), "%v", p.bondedShareExRate())
// test the value of candidate shares
assert.True(t, pool.bondedShareExRate().Equal(sdk.OneRat), "%v", pool.bondedShareExRate())
//initialSupply := p.TotalSupply
//initialUnbonded := p.TotalSupply - p.BondedPool
initialSupply := pool.TotalSupply
initialUnbonded := pool.TotalSupply - pool.BondedPool
//// process the provisions a year
//for hr := 0; hr < 8766; hr++ {
//expInflation := nextInflation(gs, params).Round(1000000000)
//expProvisions := (expInflation.Mul(sdk.NewRat(gs.TotalSupply)).Quo(hrsPerYr)).Evaluate()
//startBondedPool := p.BondedPool
//startTotalSupply := p.TotalSupply
//processProvisions(ctx, keeper, p, params)
//assert.Equal(t, startBondedPool+expProvisions, p.BondedPool)
//assert.Equal(t, startTotalSupply+expProvisions, p.TotalSupply)
//}
//assert.NotEqual(t, initialSupply, p.TotalSupply)
//assert.Equal(t, initialUnbonded, p.UnbondedPool)
////panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", p.TotalSupply, p.BondedPool, p.TotalSupply-gs.BondedPool))
// process the provisions a year
for hr := 0; hr < 8766; hr++ {
pool := keeper.GetPool(ctx)
expInflation := keeper.nextInflation(ctx).Round(1000000000)
expProvisions := (expInflation.Mul(sdk.NewRat(pool.TotalSupply)).Quo(hrsPerYrRat)).Evaluate()
startBondedPool := pool.BondedPool
startTotalSupply := pool.TotalSupply
pool = keeper.processProvisions(ctx)
keeper.setPool(ctx, pool)
//fmt.Printf("hr %v, startBondedPool %v, expProvisions %v, pool.BondedPool %v\n", hr, startBondedPool, expProvisions, pool.BondedPool)
require.Equal(t, startBondedPool+expProvisions, pool.BondedPool, "hr %v", hr)
require.Equal(t, startTotalSupply+expProvisions, pool.TotalSupply)
}
pool = keeper.GetPool(ctx)
assert.NotEqual(t, initialSupply, pool.TotalSupply)
assert.Equal(t, initialUnbonded, pool.UnbondedPool)
//panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", p.TotalSupply, p.BondedPool, pool.TotalSupply-pool.BondedPool))
//// initial bonded ratio ~ 35% ~ 30% increase for bonded holders
//assert.True(t, p.bondedRatio().Equal(sdk.NewRat(105906511, 305906511)), "%v", p.bondedRatio())
// initial bonded ratio ~ from 27% to 40% increase for bonded holders ownership of total supply
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(271734723, 671734723)), "%v", pool.bondedRatio())
//// global supply
//assert.Equal(t, int64(611813022), p.TotalSupply)
//assert.Equal(t, int64(211813022), p.BondedPool)
//assert.Equal(t, unbondedShares, p.UnbondedPool)
// global supply
assert.Equal(t, int64(671734723), pool.TotalSupply)
assert.Equal(t, int64(271734723), pool.BondedPool)
assert.Equal(t, unbondedShares, pool.UnbondedPool)
//// test the value of candidate shares
//assert.True(t, p.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(211813022)), "%v", p.bondedShareExRate())
// test the value of candidate shares
assert.True(t, pool.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(271734723)), "%v", pool.bondedShareExRate())
//}
}

View File

@ -42,8 +42,6 @@ type Pool struct {
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
}
// XXX define globalstate interface?
func initialPool() Pool {
return Pool{
TotalSupply: 0,
@ -56,30 +54,6 @@ func initialPool() Pool {
}
}
// get the bond ratio of the global state
func (p Pool) bondedRatio() sdk.Rat {
if p.TotalSupply > 0 {
return sdk.NewRat(p.BondedPool, p.TotalSupply)
}
return sdk.ZeroRat
}
// get the exchange rate of bonded token per issued share
func (p Pool) bondedShareExRate() sdk.Rat {
if p.BondedShares.IsZero() {
return sdk.OneRat
}
return sdk.NewRat(p.BondedPool).Quo(p.BondedShares)
}
// get the exchange rate of unbonded tokens held in candidates per issued share
func (p Pool) unbondedShareExRate() sdk.Rat {
if p.UnbondedShares.IsZero() {
return sdk.OneRat
}
return sdk.NewRat(p.UnbondedPool).Quo(p.UnbondedShares)
}
//_______________________________________________________________________________________________________
// CandidateStatus - status of a validator-candidate