fixed merge conflicts

This commit is contained in:
David Kajpust 2018-06-06 14:14:51 -04:00
commit 33a5e01264
103 changed files with 2890 additions and 847 deletions

View File

@ -85,6 +85,22 @@ jobs:
export PATH="$GOBIN:$PATH" export PATH="$GOBIN:$PATH"
make test_unit make test_unit
test_cli:
<<: *defaults
parallelism: 1
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Test cli
command: |
export PATH="$GOBIN:$PATH"
make test_cli
test_cover: test_cover:
<<: *defaults <<: *defaults
parallelism: 4 parallelism: 4
@ -138,6 +154,9 @@ workflows:
- lint: - lint:
requires: requires:
- setup_dependencies - setup_dependencies
- test_cli:
requires:
- setup_dependencies
- test_unit: - test_unit:
requires: requires:
- setup_dependencies - setup_dependencies

View File

@ -4,3 +4,5 @@
* [ ] Updated all code comments where relevant * [ ] Updated all code comments where relevant
* [ ] Wrote tests * [ ] Wrote tests
* [ ] Updated CHANGELOG.md * [ ] Updated CHANGELOG.md
* [ ] Updated Basecoin / other examples
* [ ] Squashed related commits and prefixed with PR number per [coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr)

5
.gitignore vendored
View File

@ -19,8 +19,11 @@ baseapp/data/*
coverage.txt coverage.txt
profile.out profile.out
### Vagrant ### # Vagrant
.vagrant/ .vagrant/
*.box *.box
*.log *.log
vagrant vagrant
# Graphviz
dependency-graph.png

View File

@ -1,30 +1,18 @@
# Changelog # Changelog
## 0.18.1
BREAKING CHANGES BREAKING CHANGES
* [x/auth] move stuff specific to auth anteHandler to the auth module rather than the types folder. This includes: FEATURES
* StdTx (and its related stuff i.e. StdSignDoc, etc)
* StdFee
* StdSignature
* Account interface
* Related to this organization, I also:
* [x/auth] got rid of AccountMapper interface (in favor of the struct already in auth module)
* [x/auth] removed the FeeHandler function from the AnteHandler, Replaced with FeeKeeper
* [x/auth] Removed GetSignatures() from Tx interface (as different Tx styles might use something different than StdSignature)
* [store] Removed SubspaceIterator and ReverseSubspaceIterator from KVStore interface and replaced them with helper functions in /types
* Switch to bech32cosmos on all human readable inputs and outputs
BUG FIXES IMPROVEMENTS
* export command now writes current validator set for Tendermint
* auto-sequencing transactions correctly FIXES
* query sequence via account store * [lcd] Switch to bech32 for addresses on all human readable inputs and outputs
* fixed duplicate pub_key in stake.Validator
## 0.18.0 ## 0.18.0
_TBD_ _2018-06-05_
BREAKING CHANGES BREAKING CHANGES
@ -43,6 +31,20 @@ BREAKING CHANGES
* Introduction of Unbonding fields, lowlevel logic throughout (not fully implemented with queue) * Introduction of Unbonding fields, lowlevel logic throughout (not fully implemented with queue)
* Introduction of PoolShares type within validators, * Introduction of PoolShares type within validators,
replaces three rational fields (BondedShares, UnbondingShares, UnbondedShares replaces three rational fields (BondedShares, UnbondingShares, UnbondedShares
* [x/auth] move stuff specific to auth anteHandler to the auth module rather than the types folder. This includes:
* StdTx (and its related stuff i.e. StdSignDoc, etc)
* StdFee
* StdSignature
* Account interface
* Related to this organization, I also:
* [x/auth] got rid of AccountMapper interface (in favor of the struct already in auth module)
* [x/auth] removed the FeeHandler function from the AnteHandler, Replaced with FeeKeeper
* [x/auth] Removed GetSignatures() from Tx interface (as different Tx styles might use something different than StdSignature)
* [store] Removed SubspaceIterator and ReverseSubspaceIterator from KVStore interface and replaced them with helper functions in /types
* [cli] rearranged commands under subcommands
* [stake] remove Tick and add EndBlocker
* Switch to bech32cosmos on all human readable inputs and outputs
FEATURES FEATURES
@ -56,14 +58,25 @@ FEATURES
* [stake] Creation of a validator/delegation generics in `/types` * [stake] Creation of a validator/delegation generics in `/types`
* [stake] Helper Description of the store in x/stake/store.md * [stake] Helper Description of the store in x/stake/store.md
* [stake] removed use of caches in the stake keeper * [stake] removed use of caches in the stake keeper
* [stake] Added REST API
* [Makefile] Added terraform/ansible playbooks to easily create remote testnets on Digital Ocean * [Makefile] Added terraform/ansible playbooks to easily create remote testnets on Digital Ocean
BUG FIXES BUG FIXES
* Auto-sequencing now works correctly
* [stake] staking delegator shares exchange rate now relative to equivalent-bonded-tokens the validator has instead of bonded tokens * [stake] staking delegator shares exchange rate now relative to equivalent-bonded-tokens the validator has instead of bonded tokens
^ this is important for unbonded validators in the power store! ^ this is important for unbonded validators in the power store!
* [cli] fixed cli-bash tests
* [ci] added cli-bash tests
* [basecoin] updated basecoin for stake and slashing
* [docs] fixed references to old cli commands
* [docs] Downgraded Swagger to v2 for downstream compatibility * [docs] Downgraded Swagger to v2 for downstream compatibility
* auto-sequencing transactions correctly
* query sequence via account store
* fixed duplicate pub_key in stake.Validator
* Auto-sequencing now works correctly
## 0.17.2 ## 0.17.2
@ -87,7 +100,7 @@ FEATURES
* [gaiacli] Support queries for candidates, delegator-bonds * [gaiacli] Support queries for candidates, delegator-bonds
* [gaiad] Added `gaiad export` command to export current state to JSON * [gaiad] Added `gaiad export` command to export current state to JSON
* [x/bank] Tx tags with sender/recipient for indexing & later retrieval * [x/bank] Tx tags with sender/recipient for indexing & later retrieval
* [x/stake] Tx tags with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit candidacy * [x/stake] Tx tags with delegator/candidate for delegation & unbonding, and candidate info for declare candidate / edit validator
IMPROVEMENTS IMPROVEMENTS
@ -103,6 +116,7 @@ BUG FIXES
* Auto-sequencing now works correctly * Auto-sequencing now works correctly
## 0.16.0 (May 14th, 2018) ## 0.16.0 (May 14th, 2018)
BREAKING CHANGES BREAKING CHANGES
@ -121,7 +135,7 @@ BREAKING CHANGES
FEATURES: FEATURES:
* Gaia stake commands include, DeclareCandidacy, EditCandidacy, Delegate, Unbond * Gaia stake commands include, CreateValidator, EditValidator, Delegate, Unbond
* MountStoreWithDB without providing a custom store works. * MountStoreWithDB without providing a custom store works.
* Repo is now lint compliant / GoMetaLinter with tendermint-lint integrated into CI * Repo is now lint compliant / GoMetaLinter with tendermint-lint integrated into CI
* Better key output, pubkey go-amino hex bytes now output by default * Better key output, pubkey go-amino hex bytes now output by default
@ -137,12 +151,14 @@ BUG FIXES
* Gaia now uses stake, ported from github.com/cosmos/gaia * Gaia now uses stake, ported from github.com/cosmos/gaia
## 0.15.1 (April 29, 2018) ## 0.15.1 (April 29, 2018)
IMPROVEMENTS: IMPROVEMENTS:
* Update Tendermint to v0.19.1 (includes many rpc fixes) * Update Tendermint to v0.19.1 (includes many rpc fixes)
## 0.15.0 (April 29, 2018) ## 0.15.0 (April 29, 2018)
NOTE: v0.15.0 is a large breaking change that updates the encoding scheme to use NOTE: v0.15.0 is a large breaking change that updates the encoding scheme to use

52
Gopkg.lock generated
View File

@ -11,13 +11,13 @@
branch = "master" branch = "master"
name = "github.com/btcsuite/btcd" name = "github.com/btcsuite/btcd"
packages = ["btcec"] packages = ["btcec"]
revision = "1432d294a5b055c297457c25434efbf13384cc46" revision = "86fed781132ac890ee03e906e4ecd5d6fa180c64"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "github.com/cosmos/bech32cosmos" name = "github.com/btcsuite/btcutil"
packages = ["go"] packages = ["bech32"]
revision = "efca97cd8c0852c44d96dfdcc70565c306eddff0" revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[projects]] [[projects]]
name = "github.com/davecgh/go-spew" name = "github.com/davecgh/go-spew"
@ -256,7 +256,7 @@
"leveldb/table", "leveldb/table",
"leveldb/util" "leveldb/util"
] ]
revision = "e6d6b529196422703d54ff5c40e79809ec2020b3" revision = "5d6fca44a948d2be89a9702de7717f0168403d3d"
[[projects]] [[projects]]
name = "github.com/tendermint/abci" name = "github.com/tendermint/abci"
@ -267,8 +267,8 @@
"server", "server",
"types" "types"
] ]
revision = "78a8905690ef54f9d57e3b2b0ee7ad3a04ef3f1f" revision = "9af8b7a7c87478869f8c280ed9539470b8f470b4"
version = "v0.10.3" version = "v0.11.0-rc4"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -298,17 +298,14 @@
revision = "915416979bf70efa4bcbf1c6cd5d64c5fff9fc19" revision = "915416979bf70efa4bcbf1c6cd5d64c5fff9fc19"
version = "v0.6.2" version = "v0.6.2"
[[projects]]
name = "github.com/tendermint/go-wire"
packages = ["."]
revision = "fa721242b042ecd4c6ed1a934ee740db4f74e45c"
version = "v0.7.3"
[[projects]] [[projects]]
name = "github.com/tendermint/iavl" name = "github.com/tendermint/iavl"
packages = ["."] packages = [
revision = "fd37a0fa3a7454423233bc3d5ea828f38e0af787" ".",
version = "v0.7.0" "sha256truncated"
]
revision = "c9206995e8f948e99927f5084a88a7e94ca256da"
version = "v0.8.0-rc0"
[[projects]] [[projects]]
name = "github.com/tendermint/tendermint" name = "github.com/tendermint/tendermint"
@ -319,6 +316,9 @@
"consensus", "consensus",
"consensus/types", "consensus/types",
"evidence", "evidence",
"libs/events",
"libs/pubsub",
"libs/pubsub/query",
"lite", "lite",
"lite/client", "lite/client",
"lite/errors", "lite/errors",
@ -347,13 +347,15 @@
"types/priv_validator", "types/priv_validator",
"version" "version"
] ]
revision = "018e096748bafe1d2d1e69b909e4158f3b26f6b2" revision = "b5baab0238c9ec26e3b2d229b0243f9ff9220bdb"
version = "v0.19.5-rc1" version = "v0.20.0-rc3"
[[projects]] [[projects]]
branch = "develop"
name = "github.com/tendermint/tmlibs" name = "github.com/tendermint/tmlibs"
packages = [ packages = [
"autofile", "autofile",
"bech32",
"cli", "cli",
"cli/flags", "cli/flags",
"clist", "clist",
@ -362,11 +364,9 @@
"flowrate", "flowrate",
"log", "log",
"merkle", "merkle",
"pubsub", "merkle/tmhash"
"pubsub/query"
] ]
revision = "cc5f287c4798ffe88c04d02df219ecb6932080fd" revision = "44f1bdb0d55cc6527e38d0a7aab406e2580f56a4"
version = "v0.8.3-rc0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -382,7 +382,7 @@
"ripemd160", "ripemd160",
"salsa20/salsa" "salsa20/salsa"
] ]
revision = "1a580b3eff7814fc9b40602fd35256c63b50f491" revision = "5ba7f63082460102a45837dbd1827e10f9479ac0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -396,13 +396,13 @@
"internal/timeseries", "internal/timeseries",
"trace" "trace"
] ]
revision = "57065200b4b034a1c8ad54ff77069408c2218ae6" revision = "1e491301e022f8f977054da4c2d852decd59571f"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "golang.org/x/sys" name = "golang.org/x/sys"
packages = ["unix"] packages = ["unix"]
revision = "7c87d13f8e835d2fb3a70a2912c811ed0c1d241b" revision = "c11f84a56e43e20a78cee75a7c034031ecf57d1f"
[[projects]] [[projects]]
name = "golang.org/x/text" name = "golang.org/x/text"
@ -463,6 +463,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "f0c6224dc5f30c1a7dea716d619665831ea0932b0eb9afc6ac897dbc459134fa" inputs-digest = "ccb2ab7644a38c2d0326280582f758256e37fc98c3ef0403581e3b85cff42188"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View File

@ -54,43 +54,33 @@
[[constraint]] [[constraint]]
name = "github.com/tendermint/abci" name = "github.com/tendermint/abci"
version = "~0.10.3" version = "=0.11.0-rc4"
[[constraint]] [[constraint]]
name = "github.com/tendermint/go-crypto" name = "github.com/tendermint/go-crypto"
version = "~0.6.2" version = "~0.6.2"
[[override]]
name = "github.com/tendermint/go-wire"
version = "0.7.3"
[[constraint]] [[constraint]]
name = "github.com/tendermint/go-amino" name = "github.com/tendermint/go-amino"
version = "~0.9.9" version = "=0.9.9"
[[constraint]] [[constraint]]
name = "github.com/tendermint/iavl" name = "github.com/tendermint/iavl"
version = "~0.7.0" version = "0.8.0-rc0"
[[constraint]] [[constraint]]
name = "github.com/tendermint/tendermint" name = "github.com/tendermint/tendermint"
version = "0.19.5-rc1" version = "=0.20.0-rc3"
[[override]] [[override]]
name = "github.com/tendermint/tmlibs" name = "github.com/tendermint/tmlibs"
version = "~0.8.3-rc0" branch = "develop"
# this got updated and broke, so locked to an old working commit ... # this got updated and broke, so locked to an old working commit ...
[[override]] [[override]]
name = "google.golang.org/genproto" name = "google.golang.org/genproto"
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200" revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
[[constraint]]
name = "github.com/cosmos/bech32cosmos"
branch = "master"
[prune] [prune]
go-tests = true go-tests = true
unused-packages = true unused-packages = true

View File

@ -70,7 +70,7 @@ get_vendor_deps:
draw_deps: draw_deps:
@# requires brew install graphviz or apt-get install graphviz @# requires brew install graphviz or apt-get install graphviz
go get github.com/RobotsAndPencils/goviz go get github.com/RobotsAndPencils/goviz
@goviz -i github.com/tendermint/tendermint/cmd/tendermint -d 3 | dot -Tpng -o dependency-graph.png @goviz -i github.com/cosmos/cosmos-sdk/cmd/gaia/cmd/gaiad -d 2 | dot -Tpng -o dependency-graph.png
######################################## ########################################

View File

@ -68,6 +68,7 @@ type BaseApp struct {
checkState *state // for CheckTx checkState *state // for CheckTx
deliverState *state // for DeliverTx deliverState *state // for DeliverTx
valUpdates []abci.Validator // cached validator changes from DeliverTx valUpdates []abci.Validator // cached validator changes from DeliverTx
signedValidators []abci.SigningValidator // absent validators from begin block
} }
var _ abci.Application = (*BaseApp)(nil) var _ abci.Application = (*BaseApp)(nil)
@ -384,6 +385,8 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
if app.beginBlocker != nil { if app.beginBlocker != nil {
res = app.beginBlocker(app.deliverState.ctx, req) res = app.beginBlocker(app.deliverState.ctx, req)
} }
// set the signed validators for addition to context in deliverTx
app.signedValidators = req.Validators
return return
} }
@ -493,6 +496,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
ctx = app.checkState.ctx.WithTxBytes(txBytes) ctx = app.checkState.ctx.WithTxBytes(txBytes)
} else { } else {
ctx = app.deliverState.ctx.WithTxBytes(txBytes) ctx = app.deliverState.ctx.WithTxBytes(txBytes)
ctx = ctx.WithSigningValidators(app.signedValidators)
} }
// Simulate a DeliverTx for gas calculation // Simulate a DeliverTx for gas calculation

View File

@ -1,7 +1,6 @@
package baseapp package baseapp
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
@ -12,6 +11,7 @@ import (
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@ -83,18 +83,36 @@ func TestLoadVersion(t *testing.T) {
header := abci.Header{Height: 1} header := abci.Header{Height: 1}
app.BeginBlock(abci.RequestBeginBlock{Header: header}) app.BeginBlock(abci.RequestBeginBlock{Header: header})
res := app.Commit() res := app.Commit()
commitID := sdk.CommitID{1, res.Data} commitID1 := sdk.CommitID{1, res.Data}
header = abci.Header{Height: 2}
app.BeginBlock(abci.RequestBeginBlock{Header: header})
res = app.Commit()
commitID2 := sdk.CommitID{2, res.Data}
// reload // reload with LoadLatestVersion
app = NewBaseApp(name, nil, logger, db) app = NewBaseApp(name, nil, logger, db)
app.MountStoresIAVL(capKey) app.MountStoresIAVL(capKey)
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil err = app.LoadLatestVersion(capKey)
assert.Nil(t, err) assert.Nil(t, err)
testLoadVersionHelper(t, app, int64(2), commitID2)
lastHeight = app.LastBlockHeight() // reload with LoadVersion, see if you can commit the same block and get
lastID = app.LastCommitID() // the same result
assert.Equal(t, int64(1), lastHeight) app = NewBaseApp(name, nil, logger, db)
assert.Equal(t, commitID, lastID) app.MountStoresIAVL(capKey)
err = app.LoadVersion(1, capKey)
assert.Nil(t, err)
testLoadVersionHelper(t, app, int64(1), commitID1)
app.BeginBlock(abci.RequestBeginBlock{Header: header})
app.Commit()
testLoadVersionHelper(t, app, int64(2), commitID2)
}
func testLoadVersionHelper(t *testing.T, app *BaseApp, expectedHeight int64, expectedID sdk.CommitID) {
lastHeight := app.LastBlockHeight()
lastID := app.LastCommitID()
assert.Equal(t, expectedHeight, lastHeight)
assert.Equal(t, expectedID, lastID)
} }
// Test that the app hash is static // Test that the app hash is static
@ -206,11 +224,91 @@ func TestInitChainer(t *testing.T) {
assert.Equal(t, value, res.Value) assert.Equal(t, value, res.Value)
} }
func getStateCheckingHandler(t *testing.T, capKey *sdk.KVStoreKey, txPerHeight int, checkHeader bool) func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
counter := 0
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
store := ctx.KVStore(capKey)
// Checking state gets updated between checkTx's / DeliverTx's
// on the store within a block.
if counter > 0 {
// check previous value in store
counterBytes := []byte{byte(counter - 1)}
prevBytes := store.Get(counterBytes)
assert.Equal(t, counterBytes, prevBytes)
}
// set the current counter in the store
counterBytes := []byte{byte(counter)}
store.Set(counterBytes, counterBytes)
// check that we can see the current header
// wrapped in an if, so it can be reused between CheckTx and DeliverTx tests.
if checkHeader {
thisHeader := ctx.BlockHeader()
height := int64((counter / txPerHeight) + 1)
assert.Equal(t, height, thisHeader.Height)
}
counter++
return sdk.Result{}
}
}
// A mock transaction that has a validation which can fail.
type testTx struct {
positiveNum int64
}
const msgType2 = "testTx"
func (tx testTx) Type() string { return msgType2 }
func (tx testTx) GetMsg() sdk.Msg { return tx }
func (tx testTx) GetSignBytes() []byte { return nil }
func (tx testTx) GetSigners() []sdk.Address { return nil }
func (tx testTx) GetSignatures() []auth.StdSignature { return nil }
func (tx testTx) ValidateBasic() sdk.Error {
if tx.positiveNum >= 0 {
return nil
}
return sdk.ErrTxDecode("positiveNum should be a non-negative integer.")
}
// Test that successive CheckTx can see each others' effects // Test that successive CheckTx can see each others' effects
// on the store within a block, and that the CheckTx state // on the store within a block, and that the CheckTx state
// gets reset to the latest Committed state during Commit // gets reset to the latest Committed state during Commit
func TestCheckTx(t *testing.T) { func TestCheckTx(t *testing.T) {
// TODO // Initialize an app for testing
app := newBaseApp(t.Name())
// make a cap key and mount the store
capKey := sdk.NewKVStoreKey("main")
app.MountStoresIAVL(capKey)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return })
txPerHeight := 3
app.Router().AddRoute(msgType, getStateCheckingHandler(t, capKey, txPerHeight, false)).
AddRoute(msgType2, func(ctx sdk.Context, msg sdk.Msg) (res sdk.Result) { return })
tx := testUpdatePowerTx{} // doesn't matter
for i := 0; i < txPerHeight; i++ {
app.Check(tx)
}
// If it gets to this point, then successive CheckTx's can see the effects of
// other CheckTx's on the block. The following checks that if another block
// is committed, the CheckTx State will reset.
app.BeginBlock(abci.RequestBeginBlock{})
tx2 := testTx{}
for i := 0; i < txPerHeight; i++ {
app.Deliver(tx2)
}
app.EndBlock(abci.RequestEndBlock{})
app.Commit()
checkStateStore := app.checkState.ctx.KVStore(capKey)
for i := 0; i < txPerHeight; i++ {
storedValue := checkStateStore.Get([]byte{byte(i)})
assert.Nil(t, storedValue)
}
} }
// Test that successive DeliverTx can see each others' effects // Test that successive DeliverTx can see each others' effects
@ -224,30 +322,9 @@ func TestDeliverTx(t *testing.T) {
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err) assert.Nil(t, err)
counter := 0
txPerHeight := 2 txPerHeight := 2
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return }) app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return })
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { app.Router().AddRoute(msgType, getStateCheckingHandler(t, capKey, txPerHeight, true))
store := ctx.KVStore(capKey)
if counter > 0 {
// check previous value in store
counterBytes := []byte{byte(counter - 1)}
prevBytes := store.Get(counterBytes)
assert.Equal(t, prevBytes, counterBytes)
}
// set the current counter in the store
counterBytes := []byte{byte(counter)}
store.Set(counterBytes, counterBytes)
// check we can see the current header
thisHeader := ctx.BlockHeader()
height := int64((counter / txPerHeight) + 1)
assert.Equal(t, height, thisHeader.Height)
counter++
return sdk.Result{}
})
tx := testUpdatePowerTx{} // doesn't matter tx := testUpdatePowerTx{} // doesn't matter
header := abci.Header{AppHash: []byte("apphash")} header := abci.Header{AppHash: []byte("apphash")}
@ -325,6 +402,27 @@ func TestSimulateTx(t *testing.T) {
} }
} }
func TestRunInvalidTransaction(t *testing.T) {
// Initialize an app for testing
app := newBaseApp(t.Name())
// make a cap key and mount the store
capKey := sdk.NewKVStoreKey("main")
app.MountStoresIAVL(capKey)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return })
app.Router().AddRoute(msgType2, func(ctx sdk.Context, msg sdk.Msg) (res sdk.Result) { return })
app.BeginBlock(abci.RequestBeginBlock{})
// Transaction where validate fails
invalidTx := testTx{-1}
err1 := app.Deliver(invalidTx)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeTxDecode), err1.Code)
// Transaction with no known route
unknownRouteTx := testUpdatePowerTx{}
err2 := app.Deliver(unknownRouteTx)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), err2.Code)
}
// Test that transactions exceeding gas limits fail // Test that transactions exceeding gas limits fail
func TestTxGasLimits(t *testing.T) { func TestTxGasLimits(t *testing.T) {
logger := defaultLogger() logger := defaultLogger()
@ -510,15 +608,20 @@ func TestValidatorChange(t *testing.T) {
// Assert that validator updates are correct. // Assert that validator updates are correct.
for _, val := range valSet { for _, val := range valSet {
pubkey, err := tmtypes.PB2TM.PubKey(val.PubKey)
// Sanity // Sanity
assert.NotEqual(t, len(val.PubKey), 0) assert.Nil(t, err)
// Find matching update and splice it out. // Find matching update and splice it out.
for j := 0; j < len(valUpdates); { for j := 0; j < len(valUpdates); j++ {
valUpdate := valUpdates[j] valUpdate := valUpdates[j]
updatePubkey, err := tmtypes.PB2TM.PubKey(valUpdate.PubKey)
assert.Nil(t, err)
// Matched. // Matched.
if bytes.Equal(valUpdate.PubKey, val.PubKey) { if updatePubkey.Equals(pubkey) {
assert.Equal(t, valUpdate.Power, val.Power+1) assert.Equal(t, valUpdate.Power, val.Power+1)
if j < len(valUpdates)-1 { if j < len(valUpdates)-1 {
// Splice it out. // Splice it out.
@ -528,7 +631,6 @@ func TestValidatorChange(t *testing.T) {
} }
// Not matched. // Not matched.
j++
} }
} }
assert.Equal(t, len(valUpdates), 0, "Some validator updates were unexpected") assert.Equal(t, len(valUpdates), 0, "Some validator updates were unexpected")
@ -542,7 +644,7 @@ func randPower() int64 {
func makeVal(secret string) abci.Validator { func makeVal(secret string) abci.Validator {
return abci.Validator{ return abci.Validator{
PubKey: makePubKey(secret).Bytes(), PubKey: tmtypes.TM2PB.PubKey(makePubKey(secret)),
Power: randPower(), Power: randPower(),
} }
} }

View File

@ -59,7 +59,7 @@ func (ctx CoreContext) QuerySubspace(cdc *wire.Codec, subspace []byte, storeName
// Query from Tendermint with the provided storename and path // Query from Tendermint with the provided storename and path
func (ctx CoreContext) query(key cmn.HexBytes, storeName, endPath string) (res []byte, err error) { func (ctx CoreContext) query(key cmn.HexBytes, storeName, endPath string) (res []byte, err error) {
path := fmt.Sprintf("/store/%s/key", storeName) path := fmt.Sprintf("/store/%s/%s", storeName, endPath)
node, err := ctx.GetNode() node, err := ctx.GetNode()
if err != nil { if err != nil {
return res, err return res, err

View File

@ -102,12 +102,6 @@ func runAddCmd(cmd *cobra.Command, args []string) error {
return nil return nil
} }
// addOutput lets us json format the data
type addOutput struct {
Key keys.Info `json:"key"`
Seed string `json:"seed"`
}
func printCreate(info keys.Info, seed string) { func printCreate(info keys.Info, seed string) {
output := viper.Get(cli.OutputFlag) output := viper.Get(cli.OutputFlag)
switch output { switch output {
@ -121,7 +115,10 @@ func printCreate(info keys.Info, seed string) {
fmt.Println(seed) fmt.Println(seed)
} }
case "json": case "json":
out := addOutput{Key: info} out, err := Bech32KeyOutput(info)
if err != nil {
panic(err)
}
if !viper.GetBool(flagNoBackup) { if !viper.GetBool(flagNoBackup) {
out.Seed = seed out.Seed = seed
} }

View File

@ -4,7 +4,6 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -54,9 +53,11 @@ func QueryKeysRequestHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("[]")) w.Write([]byte("[]"))
return return
} }
keysOutput := make([]KeyOutput, len(infos)) keysOutput, err := Bech32KeysOutput(infos)
for i, info := range infos { if err != nil {
keysOutput[i] = KeyOutput{Name: info.Name, Address: sdk.Address(info.PubKey.Address().Bytes())} w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
} }
output, err := json.MarshalIndent(keysOutput, "", " ") output, err := json.MarshalIndent(keysOutput, "", " ")
if err != nil { if err != nil {

View File

@ -4,7 +4,6 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/gorilla/mux" "github.com/gorilla/mux"
keys "github.com/tendermint/go-crypto/keys" keys "github.com/tendermint/go-crypto/keys"
@ -51,7 +50,12 @@ func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
keyOutput := KeyOutput{Name: info.Name, Address: sdk.Address(info.PubKey.Address())} keyOutput, err := Bech32KeyOutput(info)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
output, err := json.MarshalIndent(keyOutput, "", " ") output, err := json.MarshalIndent(keyOutput, "", " ")
if err != nil { if err != nil {
w.WriteHeader(500) w.WriteHeader(500)

View File

@ -1,13 +1,11 @@
package keys package keys
import ( import (
"encoding/json"
"fmt" "fmt"
"path/filepath" "path/filepath"
"github.com/spf13/viper" "github.com/spf13/viper"
crypto "github.com/tendermint/go-crypto"
keys "github.com/tendermint/go-crypto/keys" keys "github.com/tendermint/go-crypto/keys"
"github.com/tendermint/tmlibs/cli" "github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
@ -49,34 +47,52 @@ func SetKeyBase(kb keys.Keybase) {
// used for outputting keys.Info over REST // used for outputting keys.Info over REST
type KeyOutput struct { type KeyOutput struct {
Name string `json:"name"` Name string `json:"name"`
Address sdk.Address `json:"address"` Address string `json:"address"`
PubKey crypto.PubKey `json:"pub_key"` PubKey string `json:"pub_key"`
Seed string `json:"seed,omitempty"`
} }
func NewKeyOutput(info keys.Info) KeyOutput { // create a list of KeyOutput in bech32 format
return KeyOutput{ func Bech32KeysOutput(infos []keys.Info) ([]KeyOutput, error) {
Name: info.Name,
Address: sdk.Address(info.PubKey.Address().Bytes()),
PubKey: info.PubKey,
}
}
func NewKeyOutputs(infos []keys.Info) []KeyOutput {
kos := make([]KeyOutput, len(infos)) kos := make([]KeyOutput, len(infos))
for i, info := range infos { for i, info := range infos {
kos[i] = NewKeyOutput(info) ko, err := Bech32KeyOutput(info)
if err != nil {
return nil, err
} }
return kos kos[i] = ko
}
return kos, nil
}
// create a KeyOutput in bech32 format
func Bech32KeyOutput(info keys.Info) (KeyOutput, error) {
bechAccount, err := sdk.Bech32ifyAcc(sdk.Address(info.PubKey.Address().Bytes()))
if err != nil {
return KeyOutput{}, err
}
bechPubKey, err := sdk.Bech32ifyAccPub(info.PubKey)
if err != nil {
return KeyOutput{}, err
}
return KeyOutput{
Name: info.Name,
Address: bechAccount,
PubKey: bechPubKey,
}, nil
} }
func printInfo(info keys.Info) { func printInfo(info keys.Info) {
ko := NewKeyOutput(info) ko, err := Bech32KeyOutput(info)
if err != nil {
panic(err)
}
switch viper.Get(cli.OutputFlag) { switch viper.Get(cli.OutputFlag) {
case "text": case "text":
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n") fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
printKeyOutput(ko) printKeyOutput(ko)
case "json": case "json":
out, err := json.MarshalIndent(ko, "", "\t") out, err := MarshalJSON(ko)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -85,7 +101,10 @@ func printInfo(info keys.Info) {
} }
func printInfos(infos []keys.Info) { func printInfos(infos []keys.Info) {
kos := NewKeyOutputs(infos) kos, err := Bech32KeysOutput(infos)
if err != nil {
panic(err)
}
switch viper.Get(cli.OutputFlag) { switch viper.Get(cli.OutputFlag) {
case "text": case "text":
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n") fmt.Printf("NAME:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n")
@ -93,7 +112,7 @@ func printInfos(infos []keys.Info) {
printKeyOutput(ko) printKeyOutput(ko)
} }
case "json": case "json":
out, err := json.MarshalIndent(kos, "", "\t") out, err := MarshalJSON(kos)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -102,13 +121,5 @@ func printInfos(infos []keys.Info) {
} }
func printKeyOutput(ko KeyOutput) { func printKeyOutput(ko KeyOutput) {
bechAccount, err := sdk.Bech32CosmosifyAcc(ko.Address) fmt.Printf("%s\t%s\t%s\n", ko.Name, ko.Address, ko.PubKey)
if err != nil {
panic(err)
}
bechPubKey, err := sdk.Bech32CosmosifyAccPub(ko.PubKey)
if err != nil {
panic(err)
}
fmt.Printf("%s\t%s\t%s\n", ko.Name, bechAccount, bechPubKey)
} }

View File

@ -2,6 +2,7 @@ package lcd
import ( import (
"bytes" "bytes"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -16,6 +17,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
cryptoKeys "github.com/tendermint/go-crypto/keys" cryptoKeys "github.com/tendermint/go-crypto/keys"
tmcfg "github.com/tendermint/tendermint/config" tmcfg "github.com/tendermint/tendermint/config"
nm "github.com/tendermint/tendermint/node" nm "github.com/tendermint/tendermint/node"
@ -31,21 +33,30 @@ import (
client "github.com/cosmos/cosmos-sdk/client" client "github.com/cosmos/cosmos-sdk/client"
keys "github.com/cosmos/cosmos-sdk/client/keys" keys "github.com/cosmos/cosmos-sdk/client/keys"
bapp "github.com/cosmos/cosmos-sdk/examples/basecoin/app" rpc "github.com/cosmos/cosmos-sdk/client/rpc"
btypes "github.com/cosmos/cosmos-sdk/examples/basecoin/types" gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/server"
tests "github.com/cosmos/cosmos-sdk/tests" tests "github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/stake"
stakerest "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
) )
var ( var (
coinDenom = "mycoin" coinDenom = "steak"
coinAmount = int64(10000000) coinAmount = int64(10000000)
validatorAddr1Hx = ""
validatorAddr2Hx = ""
validatorAddr1 = ""
validatorAddr2 = ""
// XXX bad globals // XXX bad globals
name = "test" name = "test"
password = "0123456789" password = "0123456789"
port string // XXX: but it's the int ... port string
seed string seed string
sendAddr string sendAddr string
) )
@ -92,13 +103,13 @@ func TestKeys(t *testing.T) {
err = cdc.UnmarshalJSON([]byte(body), &m) err = cdc.UnmarshalJSON([]byte(body), &m)
require.Nil(t, err) require.Nil(t, err)
sendAddrAcc, _ := sdk.GetAccAddressHex(sendAddr)
addrAcc, _ := sdk.GetAccAddressHex(addr) addrAcc, _ := sdk.GetAccAddressHex(addr)
addrBech32, _ := sdk.Bech32ifyAcc(addrAcc)
assert.Equal(t, m[0].Name, name, "Did not serve keys name correctly") assert.Equal(t, name, m[0].Name, "Did not serve keys name correctly")
assert.Equal(t, m[0].Address, sendAddrAcc, "Did not serve keys Address correctly") assert.Equal(t, sendAddr, m[0].Address, "Did not serve keys Address correctly")
assert.Equal(t, m[1].Name, newName, "Did not serve keys name correctly") assert.Equal(t, newName, m[1].Name, "Did not serve keys name correctly")
assert.Equal(t, m[1].Address, addrAcc, "Did not serve keys Address correctly") assert.Equal(t, addrBech32, m[1].Address, "Did not serve keys Address correctly")
// select key // select key
keyEndpoint := fmt.Sprintf("/keys/%s", newName) keyEndpoint := fmt.Sprintf("/keys/%s", newName)
@ -109,7 +120,7 @@ func TestKeys(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
assert.Equal(t, newName, m2.Name, "Did not serve keys name correctly") assert.Equal(t, newName, m2.Name, "Did not serve keys name correctly")
assert.Equal(t, addrAcc, m2.Address, "Did not serve keys Address correctly") assert.Equal(t, addrBech32, m2.Address, "Did not serve keys Address correctly")
// update key // update key
jsonStr = []byte(fmt.Sprintf(`{"old_password":"%s", "new_password":"12345678901"}`, newPassword)) jsonStr = []byte(fmt.Sprintf(`{"old_password":"%s", "new_password":"12345678901"}`, newPassword))
@ -191,7 +202,7 @@ func TestBlock(t *testing.T) {
func TestValidators(t *testing.T) { func TestValidators(t *testing.T) {
var resultVals ctypes.ResultValidators var resultVals rpc.ResultValidatorsOutput
res, body := request(t, port, "GET", "/validatorsets/latest", nil) res, body := request(t, port, "GET", "/validatorsets/latest", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
@ -199,7 +210,10 @@ func TestValidators(t *testing.T) {
err := cdc.UnmarshalJSON([]byte(body), &resultVals) err := cdc.UnmarshalJSON([]byte(body), &resultVals)
require.Nil(t, err, "Couldn't parse validatorset") require.Nil(t, err, "Couldn't parse validatorset")
assert.NotEqual(t, ctypes.ResultValidators{}, resultVals) assert.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals)
assert.Contains(t, resultVals.Validators[0].Address, "cosmosvaladdr")
assert.Contains(t, resultVals.Validators[0].PubKey, "cosmosvalpub")
// -- // --
@ -209,7 +223,7 @@ func TestValidators(t *testing.T) {
err = cdc.UnmarshalJSON([]byte(body), &resultVals) err = cdc.UnmarshalJSON([]byte(body), &resultVals)
require.Nil(t, err, "Couldn't parse validatorset") require.Nil(t, err, "Couldn't parse validatorset")
assert.NotEqual(t, ctypes.ResultValidators{}, resultVals) assert.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals)
// -- // --
@ -218,9 +232,11 @@ func TestValidators(t *testing.T) {
} }
func TestCoinSend(t *testing.T) { func TestCoinSend(t *testing.T) {
bz, _ := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6")
someFakeAddr, _ := sdk.Bech32ifyAcc(bz)
// query empty // query empty
res, body := request(t, port, "GET", "/accounts/8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6", nil) res, body := request(t, port, "GET", "/accounts/"+someFakeAddr, nil)
require.Equal(t, http.StatusNoContent, res.StatusCode, body) require.Equal(t, http.StatusNoContent, res.StatusCode, body)
acc := getAccount(t, sendAddr) acc := getAccount(t, sendAddr)
@ -309,6 +325,62 @@ func TestTxs(t *testing.T) {
// assert.NotEqual(t, "[]", body) // assert.NotEqual(t, "[]", body)
} }
func TestValidatorsQuery(t *testing.T) {
validators := getValidators(t)
assert.Equal(t, len(validators), 2)
// make sure all the validators were found (order unknown because sorted by owner addr)
foundVal1, foundVal2 := false, false
if validators[0].Owner == validatorAddr1 || validators[1].Owner == validatorAddr1 {
foundVal1 = true
}
if validators[0].Owner == validatorAddr2 || validators[1].Owner == validatorAddr2 {
foundVal2 = true
}
assert.True(t, foundVal1, "validatorAddr1 %v, owner1 %v, owner2 %v", validatorAddr1, validators[0].Owner, validators[1].Owner)
assert.True(t, foundVal2, "validatorAddr2 %v, owner1 %v, owner2 %v", validatorAddr2, validators[0].Owner, validators[1].Owner)
}
func TestBond(t *testing.T) {
// create bond TX
resultTx := doBond(t, port, seed)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
// query sender
acc := getAccount(t, sendAddr)
coins := acc.GetCoins()
assert.Equal(t, int64(87), coins.AmountOf(coinDenom))
// query candidate
bond := getDelegation(t, sendAddr, validatorAddr1)
assert.Equal(t, "10/1", bond.Shares.String())
}
func TestUnbond(t *testing.T) {
// create unbond TX
resultTx := doUnbond(t, port, seed)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
// query sender
acc := getAccount(t, sendAddr)
coins := acc.GetCoins()
assert.Equal(t, int64(98), coins.AmountOf(coinDenom))
// query candidate
bond := getDelegation(t, sendAddr, validatorAddr1)
assert.Equal(t, "9/1", bond.Shares.String())
}
//__________________________________________________________ //__________________________________________________________
// helpers // helpers
@ -324,26 +396,18 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
var info cryptoKeys.Info
info, seed, err = kb.Create(name, password, cryptoKeys.AlgoEd25519) // XXX global seed
if err != nil {
return nil, nil, err
}
pubKey := info.PubKey
sendAddr = pubKey.Address().String() // XXX global
config := GetConfig() config := GetConfig()
config.Consensus.TimeoutCommit = 1000 config.Consensus.TimeoutCommit = 1000
config.Consensus.SkipTimeoutCommit = false config.Consensus.SkipTimeoutCommit = false
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
// logger = log.NewFilter(logger, log.AllowError()) logger = log.NewFilter(logger, log.AllowError())
privValidatorFile := config.PrivValidatorFile() privValidatorFile := config.PrivValidatorFile()
privVal := pvm.LoadOrGenFilePV(privValidatorFile) privVal := pvm.LoadOrGenFilePV(privValidatorFile)
db := dbm.NewMemDB() db := dbm.NewMemDB()
app := bapp.NewBasecoinApp(logger, db) app := gapp.NewGaiaApp(logger, db)
cdc = bapp.MakeCodec() // XXX cdc = gapp.MakeCodec() // XXX
genesisFile := config.GenesisFile() genesisFile := config.GenesisFile()
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile) genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
@ -351,25 +415,63 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
return nil, nil, err return nil, nil, err
} }
coins := sdk.Coins{{coinDenom, coinAmount}} genDoc.Validators = append(genDoc.Validators,
appState := map[string]interface{}{ tmtypes.GenesisValidator{
"accounts": []*btypes.GenesisAccount{ PubKey: crypto.GenPrivKeyEd25519().PubKey(),
{ Power: 1,
Name: "tester", Name: "val",
Address: pubKey.Address(),
Coins: coins,
}, },
}, )
}
stateBytes, err := json.Marshal(appState) pk1 := genDoc.Validators[0].PubKey
pk2 := genDoc.Validators[1].PubKey
validatorAddr1Hx = hex.EncodeToString(pk1.Address())
validatorAddr2Hx = hex.EncodeToString(pk2.Address())
validatorAddr1, _ = sdk.Bech32ifyVal(pk1.Address())
validatorAddr2, _ = sdk.Bech32ifyVal(pk2.Address())
// NOTE it's bad practice to reuse pk address for the owner address but doing in the
// test for simplicity
var appGenTxs [2]json.RawMessage
appGenTxs[0], _, _, err = gapp.GaiaAppGenTxNF(cdc, pk1, pk1.Address(), "test_val1", true)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
genDoc.AppStateJSON = stateBytes appGenTxs[1], _, _, err = gapp.GaiaAppGenTxNF(cdc, pk2, pk2.Address(), "test_val2", true)
if err != nil {
return nil, nil, err
}
genesisState, err := gapp.GaiaAppGenState(cdc, appGenTxs[:])
if err != nil {
return nil, nil, err
}
// add the sendAddr to genesis
var info cryptoKeys.Info
info, seed, err = kb.Create(name, password, cryptoKeys.AlgoEd25519) // XXX global seed
if err != nil {
return nil, nil, err
}
sendAddrHex, _ := sdk.GetAccAddressHex(info.PubKey.Address().String())
sendAddr, _ = sdk.Bech32ifyAcc(sendAddrHex) // XXX global
accAuth := auth.NewBaseAccountWithAddress(info.PubKey.Address())
accAuth.Coins = sdk.Coins{{"steak", 100}}
acc := gapp.NewGenesisAccount(&accAuth)
genesisState.Accounts = append(genesisState.Accounts, acc)
appState, err := wire.MarshalJSONIndent(cdc, genesisState)
if err != nil {
return nil, nil, err
}
genDoc.AppStateJSON = appState
// LCD listen address // LCD listen address
port = fmt.Sprintf("%d", 17377) // XXX var listenAddr string
listenAddr := fmt.Sprintf("tcp://localhost:%s", port) // XXX listenAddr, port, err = server.FreeTCPAddr()
if err != nil {
return nil, nil, err
}
// XXX: need to set this so LCD knows the tendermint node address! // XXX: need to set this so LCD knows the tendermint node address!
viper.Set(client.FlagNode, config.RPC.ListenAddress) viper.Set(client.FlagNode, config.RPC.ListenAddress)
@ -379,7 +481,7 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
lcd, err := startLCD(logger, listenAddr) lcd, err := startLCD(logger, listenAddr, cdc)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -418,7 +520,7 @@ func startTM(cfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, p
} }
// start the LCD. note this blocks! // start the LCD. note this blocks!
func startLCD(logger log.Logger, listenAddr string) (net.Listener, error) { func startLCD(logger log.Logger, listenAddr string, cdc *wire.Codec) (net.Listener, error) {
handler := createHandler(cdc) handler := createHandler(cdc)
return tmrpc.StartHTTPServer(listenAddr, handler, logger) return tmrpc.StartHTTPServer(listenAddr, handler, logger)
} }
@ -456,7 +558,7 @@ func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctype
kb := client.MockKeyBase() kb := client.MockKeyBase()
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519")) receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
require.Nil(t, err) require.Nil(t, err)
receiveAddr = receiveInfo.PubKey.Address().String() receiveAddr, _ = sdk.Bech32ifyAcc(receiveInfo.PubKey.Address())
acc := getAccount(t, sendAddr) acc := getAccount(t, sendAddr)
sequence := acc.GetSequence() sequence := acc.GetSequence()
@ -473,12 +575,11 @@ func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctype
} }
func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) { func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) {
// create receive address // create receive address
kb := client.MockKeyBase() kb := client.MockKeyBase()
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519")) receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
require.Nil(t, err) require.Nil(t, err)
receiveAddr := receiveInfo.PubKey.Address().String() receiveAddr, _ := sdk.Bech32ifyAcc(receiveInfo.PubKey.Address())
// get the account to get the sequence // get the account to get the sequence
acc := getAccount(t, sendAddr) acc := getAccount(t, sendAddr)
@ -494,3 +595,81 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
return resultTx return resultTx
} }
func getDelegation(t *testing.T, delegatorAddr, candidateAddr string) stake.Delegation {
// get the account to get the sequence
res, body := request(t, port, "GET", "/stake/"+delegatorAddr+"/bonding_status/"+candidateAddr, nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var bond stake.Delegation
err := cdc.UnmarshalJSON([]byte(body), &bond)
require.Nil(t, err)
return bond
}
func doBond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) {
// get the account to get the sequence
acc := getAccount(t, sendAddr)
sequence := acc.GetSequence()
// send
jsonStr := []byte(fmt.Sprintf(`{
"name": "%s",
"password": "%s",
"sequence": %d,
"delegate": [
{
"delegator_addr": "%s",
"validator_addr": "%s",
"bond": { "denom": "%s", "amount": 10 }
}
],
"unbond": []
}`, name, password, sequence, sendAddr, validatorAddr1, coinDenom))
res, body := request(t, port, "POST", "/stake/delegations", jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results []ctypes.ResultBroadcastTxCommit
err := cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
return results[0]
}
func doUnbond(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) {
// get the account to get the sequence
acc := getAccount(t, sendAddr)
sequence := acc.GetSequence()
// send
jsonStr := []byte(fmt.Sprintf(`{
"name": "%s",
"password": "%s",
"sequence": %d,
"bond": [],
"unbond": [
{
"delegator_addr": "%s",
"validator_addr": "%s",
"shares": "1"
}
]
}`, name, password, sequence, sendAddr, validatorAddr1))
res, body := request(t, port, "POST", "/stake/delegations", jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results []ctypes.ResultBroadcastTxCommit
err := cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
return results[0]
}
func getValidators(t *testing.T) []stakerest.StakeValidatorOutput {
// get the account to get the sequence
res, body := request(t, port, "GET", "/stake/validators", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var validators []stakerest.StakeValidatorOutput
err := cdc.UnmarshalJSON([]byte(body), &validators)
require.Nil(t, err)
return validators
}

View File

@ -22,6 +22,7 @@ import (
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
ibc "github.com/cosmos/cosmos-sdk/x/ibc/client/rest" ibc "github.com/cosmos/cosmos-sdk/x/ibc/client/rest"
stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
) )
const ( const (
@ -55,6 +56,7 @@ func startRESTServerFn(cdc *wire.Codec) func(cmd *cobra.Command, args []string)
if err != nil { if err != nil {
return err return err
} }
logger.Info("REST server started")
// Wait forever and cleanup // Wait forever and cleanup
cmn.TrapSignal(func() { cmn.TrapSignal(func() {
@ -83,5 +85,6 @@ func createHandler(cdc *wire.Codec) http.Handler {
auth.RegisterRoutes(ctx, r, cdc, "acc") auth.RegisterRoutes(ctx, r, cdc, "acc")
bank.RegisterRoutes(ctx, r, cdc, kb) bank.RegisterRoutes(ctx, r, cdc, kb)
ibc.RegisterRoutes(ctx, r, cdc, kb) ibc.RegisterRoutes(ctx, r, cdc, kb)
stake.RegisterRoutes(ctx, r, cdc, kb)
return r return r
} }

View File

@ -16,7 +16,8 @@ const (
flagSelect = "select" flagSelect = "select"
) )
func blockCommand() *cobra.Command { //BlockCommand returns the verified block data for a given heights
func BlockCommand() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "block [height]", Use: "block [height]",
Short: "Get verified data for a the block at given height", Short: "Get verified data for a the block at given height",

View File

@ -26,8 +26,6 @@ func AddCommands(cmd *cobra.Command) {
cmd.AddCommand( cmd.AddCommand(
initClientCommand(), initClientCommand(),
statusCommand(), statusCommand(),
blockCommand(),
validatorCommand(),
) )
} }

View File

@ -10,14 +10,17 @@ import (
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
tmtypes "github.com/tendermint/tendermint/types"
) )
// TODO these next two functions feel kinda hacky based on their placement // TODO these next two functions feel kinda hacky based on their placement
func validatorCommand() *cobra.Command { //ValidatorCommand returns the validator set for a given height
func ValidatorCommand() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "validatorset [height]", Use: "validator-set [height]",
Short: "Get the full validator set at given height", Short: "Get the full tendermint validator set at given height",
Args: cobra.MaximumNArgs(1), Args: cobra.MaximumNArgs(1),
RunE: printValidators, RunE: printValidators,
} }
@ -27,6 +30,38 @@ func validatorCommand() *cobra.Command {
return cmd return cmd
} }
// Validator output in bech32 format
type ValidatorOutput struct {
Address string `json:"address"` // in bech32
PubKey string `json:"pub_key"` // in bech32
Accum int64 `json:"accum"`
VotingPower int64 `json:"voting_power"`
}
// Validators at a certain height output in bech32 format
type ResultValidatorsOutput struct {
BlockHeight int64 `json:"block_height"`
Validators []ValidatorOutput `json:"validators"`
}
func bech32ValidatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error) {
bechAddress, err := sdk.Bech32ifyVal(validator.Address)
if err != nil {
return ValidatorOutput{}, err
}
bechValPubkey, err := sdk.Bech32ifyValPub(validator.PubKey)
if err != nil {
return ValidatorOutput{}, err
}
return ValidatorOutput{
Address: bechAddress,
PubKey: bechValPubkey,
Accum: validator.Accum,
VotingPower: validator.VotingPower,
}, nil
}
func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) { func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) {
// get the node // get the node
node, err := ctx.GetNode() node, err := ctx.GetNode()
@ -34,12 +69,23 @@ func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) {
return nil, err return nil, err
} }
res, err := node.Validators(height) validatorsRes, err := node.Validators(height)
if err != nil { if err != nil {
return nil, err return nil, err
} }
output, err := cdc.MarshalJSON(res) outputValidatorsRes := ResultValidatorsOutput{
BlockHeight: validatorsRes.BlockHeight,
Validators: make([]ValidatorOutput, len(validatorsRes.Validators)),
}
for i := 0; i < len(validatorsRes.Validators); i++ {
outputValidatorsRes.Validators[i], err = bech32ValidatorOutput(validatorsRes.Validators[i])
if err != nil {
return nil, err
}
}
output, err := cdc.MarshalJSON(outputValidatorsRes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -95,6 +141,7 @@ func ValidatorSetRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
return return
} }
w.Write(output) w.Write(output)
} }
} }

View File

@ -5,6 +5,7 @@ import (
"os" "os"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@ -15,6 +16,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc" "github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake"
) )
@ -38,6 +40,7 @@ type GaiaApp struct {
keyAccount *sdk.KVStoreKey keyAccount *sdk.KVStoreKey
keyIBC *sdk.KVStoreKey keyIBC *sdk.KVStoreKey
keyStake *sdk.KVStoreKey keyStake *sdk.KVStoreKey
keySlashing *sdk.KVStoreKey
// Manage getting and setting accounts // Manage getting and setting accounts
accountMapper auth.AccountMapper accountMapper auth.AccountMapper
@ -45,6 +48,7 @@ type GaiaApp struct {
coinKeeper bank.Keeper coinKeeper bank.Keeper
ibcMapper ibc.Mapper ibcMapper ibc.Mapper
stakeKeeper stake.Keeper stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
} }
func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
@ -58,6 +62,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
keyAccount: sdk.NewKVStoreKey("acc"), keyAccount: sdk.NewKVStoreKey("acc"),
keyIBC: sdk.NewKVStoreKey("ibc"), keyIBC: sdk.NewKVStoreKey("ibc"),
keyStake: sdk.NewKVStoreKey("stake"), keyStake: sdk.NewKVStoreKey("stake"),
keySlashing: sdk.NewKVStoreKey("slashing"),
} }
// define the accountMapper // define the accountMapper
@ -71,6 +76,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
app.coinKeeper = bank.NewKeeper(app.accountMapper) app.coinKeeper = bank.NewKeeper(app.accountMapper)
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace)) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace))
// register message routes // register message routes
app.Router(). app.Router().
@ -80,9 +86,10 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
// initialize BaseApp // initialize BaseApp
app.SetInitChainer(app.initChainer) app.SetInitChainer(app.initChainer)
app.SetEndBlocker(stake.NewEndBlocker(app.stakeKeeper)) app.SetBeginBlocker(app.BeginBlocker)
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake) app.SetEndBlocker(app.EndBlocker)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing)
err := app.LoadLatestVersion(app.keyMain) err := app.LoadLatestVersion(app.keyMain)
if err != nil { if err != nil {
cmn.Exit(err.Error()) cmn.Exit(err.Error())
@ -97,15 +104,35 @@ func MakeCodec() *wire.Codec {
ibc.RegisterWire(cdc) ibc.RegisterWire(cdc)
bank.RegisterWire(cdc) bank.RegisterWire(cdc)
stake.RegisterWire(cdc) stake.RegisterWire(cdc)
slashing.RegisterWire(cdc)
auth.RegisterWire(cdc) auth.RegisterWire(cdc)
sdk.RegisterWire(cdc) sdk.RegisterWire(cdc)
wire.RegisterCrypto(cdc) wire.RegisterCrypto(cdc)
return cdc return cdc
} }
// application updates every end block
func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper)
return abci.ResponseBeginBlock{
Tags: tags.ToKVPairs(),
}
}
// application updates every end block
func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper)
return abci.ResponseEndBlock{
ValidatorUpdates: validatorUpdates,
}
}
// custom logic for gaia initialization // custom logic for gaia initialization
func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes stateJSON := req.AppStateBytes
// TODO is this now the whole genesis file?
var genesisState GenesisState var genesisState GenesisState
err := app.cdc.UnmarshalJSON(stateJSON, &genesisState) err := app.cdc.UnmarshalJSON(stateJSON, &genesisState)
@ -126,8 +153,8 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci
return abci.ResponseInitChain{} return abci.ResponseInitChain{}
} }
// export the state of gaia for a genesis f // export the state of gaia for a genesis file
func (app *GaiaApp) ExportAppStateJSON() (appState json.RawMessage, err error) { func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
ctx := app.NewContext(true, abci.Header{}) ctx := app.NewContext(true, abci.Header{})
// iterate to get the accounts // iterate to get the accounts
@ -143,5 +170,10 @@ func (app *GaiaApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
Accounts: accounts, Accounts: accounts,
StakeData: stake.WriteGenesis(ctx, app.stakeKeeper), StakeData: stake.WriteGenesis(ctx, app.stakeKeeper),
} }
return wire.MarshalJSONIndent(app.cdc, genState) appState, err = wire.MarshalJSONIndent(app.cdc, genState)
if err != nil {
return nil, nil, err
}
validators = stake.WriteValidators(ctx, app.stakeKeeper)
return appState, validators, nil
} }

View File

@ -1,7 +1,6 @@
package app package app
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"testing" "testing"
@ -115,7 +114,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
// Initialize the chain // Initialize the chain
vals := []abci.Validator{} vals := []abci.Validator{}
gapp.InitChain(abci.RequestInitChain{vals, stateBytes}) gapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
gapp.Commit() gapp.Commit()
return nil return nil
@ -139,30 +138,6 @@ func TestMsgs(t *testing.T) {
} }
} }
func setGenesisAccounts(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
genaccs := make([]GenesisAccount, len(accs))
for i, acc := range accs {
genaccs[i] = NewGenesisAccount(acc)
}
genesisState := GenesisState{
Accounts: genaccs,
StakeData: stake.DefaultGenesisState(),
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
if err != nil {
return err
}
// Initialize the chain
vals := []abci.Validator{}
gapp.InitChain(abci.RequestInitChain{vals, stateBytes})
gapp.Commit()
return nil
}
func TestGenesis(t *testing.T) { func TestGenesis(t *testing.T) {
logger, dbs := loggerAndDB() logger, dbs := loggerAndDB()
gapp := NewGaiaApp(logger, dbs) gapp := NewGaiaApp(logger, dbs)
@ -178,7 +153,7 @@ func TestGenesis(t *testing.T) {
} }
err = setGenesis(gapp, baseAcc) err = setGenesis(gapp, baseAcc)
assert.Nil(t, err) require.Nil(t, err)
// A checkTx context // A checkTx context
ctx := gapp.BaseApp.NewContext(true, abci.Header{}) ctx := gapp.BaseApp.NewContext(true, abci.Header{})
@ -394,13 +369,13 @@ func TestStakeMsgs(t *testing.T) {
require.Equal(t, acc1, res1) require.Equal(t, acc1, res1)
require.Equal(t, acc2, res2) require.Equal(t, acc2, res2)
// Declare Candidacy // Create Validator
description := stake.NewDescription("foo_moniker", "", "", "") description := stake.NewDescription("foo_moniker", "", "", "")
declareCandidacyMsg := stake.NewMsgDeclareCandidacy( createValidatorMsg := stake.NewMsgCreateValidator(
addr1, priv1.PubKey(), bondCoin, description, addr1, priv1.PubKey(), bondCoin, description,
) )
SignCheckDeliver(t, gapp, declareCandidacyMsg, []int64{0}, true, priv1) SignCheckDeliver(t, gapp, createValidatorMsg, []int64{0}, true, priv1)
ctxDeliver := gapp.BaseApp.NewContext(false, abci.Header{}) ctxDeliver := gapp.BaseApp.NewContext(false, abci.Header{})
res1 = gapp.accountMapper.GetAccount(ctxDeliver, addr1) res1 = gapp.accountMapper.GetAccount(ctxDeliver, addr1)
@ -415,13 +390,13 @@ func TestStakeMsgs(t *testing.T) {
bond, found := gapp.stakeKeeper.GetDelegation(ctxDeliver, addr1, addr1) bond, found := gapp.stakeKeeper.GetDelegation(ctxDeliver, addr1, addr1)
require.True(sdk.RatEq(t, sdk.NewRat(10), bond.Shares)) require.True(sdk.RatEq(t, sdk.NewRat(10), bond.Shares))
// Edit Candidacy // Edit Validator
description = stake.NewDescription("bar_moniker", "", "", "") description = stake.NewDescription("bar_moniker", "", "", "")
editCandidacyMsg := stake.NewMsgEditCandidacy( editValidatorMsg := stake.NewMsgEditValidator(
addr1, description, addr1, description,
) )
SignDeliver(t, gapp, editCandidacyMsg, []int64{1}, true, priv1) SignDeliver(t, gapp, editValidatorMsg, []int64{1}, true, priv1)
validator, found = gapp.stakeKeeper.GetValidator(ctxDeliver, addr1) validator, found = gapp.stakeKeeper.GetValidator(ctxDeliver, addr1)
require.True(t, found) require.True(t, found)
@ -455,6 +430,42 @@ func TestStakeMsgs(t *testing.T) {
require.False(t, found) require.False(t, found)
} }
func TestExportValidators(t *testing.T) {
gapp := newGaiaApp()
genCoins, err := sdk.ParseCoins("42steak")
require.Nil(t, err)
bondCoin, err := sdk.ParseCoin("10steak")
require.Nil(t, err)
acc1 := &auth.BaseAccount{
Address: addr1,
Coins: genCoins,
}
acc2 := &auth.BaseAccount{
Address: addr2,
Coins: genCoins,
}
err = setGenesis(gapp, acc1, acc2)
require.Nil(t, err)
// Create Validator
description := stake.NewDescription("foo_moniker", "", "", "")
createValidatorMsg := stake.NewMsgCreateValidator(
addr1, priv1.PubKey(), bondCoin, description,
)
SignCheckDeliver(t, gapp, createValidatorMsg, []int64{0}, true, priv1)
gapp.Commit()
// Export validator set
_, validators, err := gapp.ExportAppStateAndValidators()
require.Nil(t, err)
require.Equal(t, 1, len(validators)) // 1 validator
require.Equal(t, priv1.PubKey(), validators[0].PubKey)
require.Equal(t, int64(10), validators[0].Power)
}
//____________________________________________________________________________________ //____________________________________________________________________________________
func CheckBalance(t *testing.T, gapp *GaiaApp, addr sdk.Address, balExpected string) { func CheckBalance(t *testing.T, gapp *GaiaApp, addr sdk.Address, balExpected string) {

View File

@ -65,7 +65,7 @@ func GaiaAppInit() server.AppInit {
fsAppGenState := pflag.NewFlagSet("", pflag.ContinueOnError) fsAppGenState := pflag.NewFlagSet("", pflag.ContinueOnError)
fsAppGenTx := pflag.NewFlagSet("", pflag.ContinueOnError) fsAppGenTx := pflag.NewFlagSet("", pflag.ContinueOnError)
fsAppGenTx.String(flagName, "", "validator moniker, if left blank, do not add validator") fsAppGenTx.String(flagName, "", "validator moniker, required")
fsAppGenTx.String(flagClientHome, DefaultCLIHome, fsAppGenTx.String(flagClientHome, DefaultCLIHome,
"home directory for the client, used for key generation") "home directory for the client, used for key generation")
fsAppGenTx.Bool(flagOWK, false, "overwrite the accounts created") fsAppGenTx.Bool(flagOWK, false, "overwrite the accounts created")
@ -74,7 +74,7 @@ func GaiaAppInit() server.AppInit {
FlagsAppGenState: fsAppGenState, FlagsAppGenState: fsAppGenState,
FlagsAppGenTx: fsAppGenTx, FlagsAppGenTx: fsAppGenTx,
AppGenTx: GaiaAppGenTx, AppGenTx: GaiaAppGenTx,
AppGenState: GaiaAppGenState, AppGenState: GaiaAppGenStateJSON,
} }
} }
@ -85,19 +85,35 @@ type GaiaGenTx struct {
PubKey crypto.PubKey `json:"pub_key"` PubKey crypto.PubKey `json:"pub_key"`
} }
// Generate a gaia genesis transaction // Generate a gaia genesis transaction with flags
func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey) ( func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) { appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
var addr sdk.Address
var secret string
clientRoot := viper.GetString(flagClientHome) clientRoot := viper.GetString(flagClientHome)
overwrite := viper.GetBool(flagOWK) overwrite := viper.GetBool(flagOWK)
name := viper.GetString(flagName) name := viper.GetString(flagName)
if name == "" {
return nil, nil, tmtypes.GenesisValidator{}, errors.New("Must specify --name (validator moniker)")
}
var addr sdk.Address
var secret string
addr, secret, err = server.GenerateSaveCoinKey(clientRoot, name, "1234567890", overwrite) addr, secret, err = server.GenerateSaveCoinKey(clientRoot, name, "1234567890", overwrite)
if err != nil { if err != nil {
return return
} }
mm := map[string]string{"secret": secret}
var bz []byte
bz, err = cdc.MarshalJSON(mm)
if err != nil {
return
}
cliPrint = json.RawMessage(bz)
return GaiaAppGenTxNF(cdc, pk, addr, name, overwrite)
}
// Generate a gaia genesis transaction without flags
func GaiaAppGenTxNF(cdc *wire.Codec, pk crypto.PubKey, addr sdk.Address, name string, overwrite bool) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
var bz []byte var bz []byte
gaiaGenTx := GaiaGenTx{ gaiaGenTx := GaiaGenTx{
@ -111,13 +127,6 @@ func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey) (
} }
appGenTx = json.RawMessage(bz) appGenTx = json.RawMessage(bz)
mm := map[string]string{"secret": secret}
bz, err = cdc.MarshalJSON(mm)
if err != nil {
return
}
cliPrint = json.RawMessage(bz)
validator = tmtypes.GenesisValidator{ validator = tmtypes.GenesisValidator{
PubKey: pk, PubKey: pk,
Power: freeFermionVal, Power: freeFermionVal,
@ -127,7 +136,7 @@ func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey) (
// Create the core parameters for genesis initialization for gaia // Create the core parameters for genesis initialization for gaia
// note that the pubkey input is this machines pubkey // note that the pubkey input is this machines pubkey
func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) { func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState GenesisState, err error) {
if len(appGenTxs) == 0 { if len(appGenTxs) == 0 {
err = errors.New("must provide at least genesis transaction") err = errors.New("must provide at least genesis transaction")
@ -171,10 +180,21 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState jso
} }
// create the final app state // create the final app state
genesisState := GenesisState{ genesisState = GenesisState{
Accounts: genaccs, Accounts: genaccs,
StakeData: stakeData, StakeData: stakeData,
} }
return
}
// GaiaAppGenState but with JSON
func GaiaAppGenStateJSON(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
// create the final app state
genesisState, err := GaiaAppGenState(cdc, appGenTxs)
if err != nil {
return nil, err
}
appState, err = wire.MarshalJSONIndent(cdc, genesisState) appState, err = wire.MarshalJSONIndent(cdc, genesisState)
return return
} }

View File

@ -30,46 +30,44 @@ func TestGaiaCLISend(t *testing.T) {
executeWrite(t, "gaiacli keys add bar", pass) executeWrite(t, "gaiacli keys add bar", pass)
// get a free port, also setup some common flags // get a free port, also setup some common flags
servAddr := server.FreeTCPAddr(t) servAddr, port, err := server.FreeTCPAddr()
require.NoError(t, err)
flags := fmt.Sprintf("--node=%v --chain-id=%v", servAddr, chainID) flags := fmt.Sprintf("--node=%v --chain-id=%v", servAddr, chainID)
// start gaiad server // start gaiad server
cmd, _, _ := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr)) proc := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr))
defer cmd.Process.Kill() defer proc.Stop(false)
tests.WaitForStart(port)
fooAddr, _ := executeGetAddrPK(t, "gaiacli keys show foo --output=json") fooAddr, _ := executeGetAddrPK(t, "gaiacli keys show foo --output=json")
fooCech, err := sdk.Bech32ifyAcc(fooAddr)
require.NoError(t, err)
barAddr, _ := executeGetAddrPK(t, "gaiacli keys show bar --output=json") barAddr, _ := executeGetAddrPK(t, "gaiacli keys show bar --output=json")
barCech, err := sdk.Bech32ifyAcc(barAddr)
require.NoError(t, err)
fooBech, err := sdk.Bech32CosmosifyAcc(fooAddr) fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooCech, flags))
if err != nil {
t.Error(err)
}
barBech, err := sdk.Bech32CosmosifyAcc(barAddr)
if err != nil {
t.Error(err)
}
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooBech, flags))
assert.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak")) assert.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak"))
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barAddr), pass) executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), pass)
time.Sleep(time.Second * 3) // waiting for some blocks to pass time.Sleep(time.Second * 2) // waiting for some blocks to pass
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barBech, flags)) barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak")) assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak"))
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooBech, flags)) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooCech, flags))
assert.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak")) assert.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak"))
// test autosequencing // test autosequencing
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barAddr), pass) executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), pass)
time.Sleep(time.Second * 3) // waiting for some blocks to pass time.Sleep(time.Second * 2) // waiting for some blocks to pass
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barBech, flags)) barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
assert.Equal(t, int64(20), barAcc.GetCoins().AmountOf("steak")) assert.Equal(t, int64(20), barAcc.GetCoins().AmountOf("steak"))
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooBech, flags)) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooCech, flags))
assert.Equal(t, int64(30), fooAcc.GetCoins().AmountOf("steak")) assert.Equal(t, int64(30), fooAcc.GetCoins().AmountOf("steak"))
} }
func TestGaiaCLIDeclareCandidacy(t *testing.T) { func TestGaiaCLICreateValidator(t *testing.T) {
tests.ExecuteT(t, "gaiad unsafe_reset_all") tests.ExecuteT(t, "gaiad unsafe_reset_all")
pass := "1234567890" pass := "1234567890"
@ -79,95 +77,79 @@ func TestGaiaCLIDeclareCandidacy(t *testing.T) {
executeWrite(t, "gaiacli keys add bar", pass) executeWrite(t, "gaiacli keys add bar", pass)
// get a free port, also setup some common flags // get a free port, also setup some common flags
servAddr := server.FreeTCPAddr(t) servAddr, port, err := server.FreeTCPAddr()
require.NoError(t, err)
flags := fmt.Sprintf("--node=%v --chain-id=%v", servAddr, chainID) flags := fmt.Sprintf("--node=%v --chain-id=%v", servAddr, chainID)
// start gaiad server // start gaiad server
cmd, _, _ := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr)) proc := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr))
defer cmd.Process.Kill() defer proc.Stop(false)
tests.WaitForStart(port)
fooAddr, _ := executeGetAddrPK(t, "gaiacli keys show foo --output=json") fooAddr, _ := executeGetAddrPK(t, "gaiacli keys show foo --output=json")
barAddr, _ := executeGetAddrPK(t, "gaiacli keys show bar --output=json") fooCech, err := sdk.Bech32ifyAcc(fooAddr)
require.NoError(t, err)
barAddr, barPubKey := executeGetAddrPK(t, "gaiacli keys show bar --output=json")
barCech, err := sdk.Bech32ifyAcc(barAddr)
require.NoError(t, err)
barCeshPubKey, err := sdk.Bech32ifyValPub(barPubKey)
require.NoError(t, err)
fooBech, err := sdk.Bech32CosmosifyAcc(fooAddr) executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), pass)
if err != nil {
t.Error(err)
}
barBech, err := sdk.Bech32CosmosifyAcc(barAddr)
if err != nil {
t.Error(err)
}
valPrivkey := crypto.GenPrivKeyEd25519()
valAddr := sdk.Address((valPrivkey.PubKey().Address()))
bechVal, err := sdk.Bech32CosmosifyVal(valAddr)
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barBech), pass)
time.Sleep(time.Second * 3) // waiting for some blocks to pass time.Sleep(time.Second * 3) // waiting for some blocks to pass
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooBech, flags)) barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
assert.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak"))
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barBech, flags))
assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak")) assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak"))
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooCech, flags))
assert.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak"))
// declare candidacy // create validator
declStr := fmt.Sprintf("gaiacli create-validator %v", flags) cvStr := fmt.Sprintf("gaiacli stake create-validator %v", flags)
declStr += fmt.Sprintf(" --name=%v", "bar") cvStr += fmt.Sprintf(" --name=%v", "bar")
declStr += fmt.Sprintf(" --validator-address=%v", bechVal) cvStr += fmt.Sprintf(" --address-validator=%v", barCech)
declStr += fmt.Sprintf(" --amount=%v", "3steak") cvStr += fmt.Sprintf(" --pubkey=%v", barCeshPubKey)
declStr += fmt.Sprintf(" --moniker=%v", "bar-vally") cvStr += fmt.Sprintf(" --amount=%v", "2steak")
fmt.Printf("debug declStr: %v\n", declStr) cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally")
executeWrite(t, declStr, pass)
time.Sleep(time.Second) // waiting for some blocks to pass executeWrite(t, cvStr, pass)
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags)) time.Sleep(time.Second * 3) // waiting for some blocks to pass
assert.Equal(t, int64(7), barAcc.GetCoins().AmountOf("steak"))
candidate := executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, barAddr)) barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
assert.Equal(t, candidate.Owner.String(), barAddr) require.Equal(t, int64(8), barAcc.GetCoins().AmountOf("steak"), "%v", barAcc)
assert.Equal(t, int64(3), candidate.PoolShares)
validator := executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %v --output=json %v", barCech, flags))
assert.Equal(t, validator.Owner, barAddr)
assert.Equal(t, "2/1", validator.PoolShares.Amount.String())
// TODO timeout issues if not connected to the internet
// unbond a single share // unbond a single share
//unbondStr := fmt.Sprintf("gaiacli unbond %v", flags) unbondStr := fmt.Sprintf("gaiacli stake unbond %v", flags)
//unbondStr += fmt.Sprintf(" --name=%v", "bar") unbondStr += fmt.Sprintf(" --name=%v", "bar")
//unbondStr += fmt.Sprintf(" --address-candidate=%v", barAddr) unbondStr += fmt.Sprintf(" --address-validator=%v", barCech)
//unbondStr += fmt.Sprintf(" --address-delegator=%v", barAddr) unbondStr += fmt.Sprintf(" --address-delegator=%v", barCech)
//unbondStr += fmt.Sprintf(" --shares=%v", "1") unbondStr += fmt.Sprintf(" --shares=%v", "1")
//unbondStr += fmt.Sprintf(" --sequence=%v", "1") unbondStr += fmt.Sprintf(" --sequence=%v", "1")
//fmt.Printf("debug unbondStr: %v\n", unbondStr) t.Log(fmt.Sprintf("debug unbondStr: %v\n", unbondStr))
//executeWrite(t, unbondStr, pass)
//time.Sleep(time.Second * 3) // waiting for some blocks to pass executeWrite(t, unbondStr, pass)
//barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags)) time.Sleep(time.Second * 3) // waiting for some blocks to pass
//assert.Equal(t, int64(99998), barAcc.GetCoins().AmountOf("steak"))
//candidate = executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, barAddr)) barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
//assert.Equal(t, int64(2), candidate.BondedShares.Evaluate()) require.Equal(t, int64(9), barAcc.GetCoins().AmountOf("steak"), "%v", barAcc)
validator = executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %v --output=json %v", barCech, flags))
assert.Equal(t, "1/1", validator.PoolShares.Amount.String())
} }
//___________________________________________________________________________________
// executors
func executeWrite(t *testing.T, cmdStr string, writes ...string) { func executeWrite(t *testing.T, cmdStr string, writes ...string) {
cmd, wc, _ := tests.GoExecuteT(t, cmdStr) proc := tests.GoExecuteT(t, cmdStr)
for _, write := range writes { for _, write := range writes {
_, err := wc.Write([]byte(write + "\n")) _, err := proc.StdinPipe.Write([]byte(write + "\n"))
if err != nil {
fmt.Println(err)
}
require.NoError(t, err) require.NoError(t, err)
} }
fmt.Printf("debug waiting cmdStr: %v\n", cmdStr) proc.Wait()
cmd.Wait()
}
func executeWritePrint(t *testing.T, cmdStr string, writes ...string) {
cmd, wc, rc := tests.GoExecuteT(t, cmdStr)
for _, write := range writes {
_, err := wc.Write([]byte(write + "\n"))
require.NoError(t, err)
}
fmt.Printf("debug waiting cmdStr: %v\n", cmdStr)
cmd.Wait()
bz := make([]byte, 100000)
rc.Read(bz)
fmt.Printf("debug read: %v\n", string(bz))
} }
func executeInit(t *testing.T, cmdStr string) (chainID string) { func executeInit(t *testing.T, cmdStr string) (chainID string) {
@ -187,7 +169,14 @@ func executeGetAddrPK(t *testing.T, cmdStr string) (sdk.Address, crypto.PubKey)
out := tests.ExecuteT(t, cmdStr) out := tests.ExecuteT(t, cmdStr)
var ko keys.KeyOutput var ko keys.KeyOutput
keys.UnmarshalJSON([]byte(out), &ko) keys.UnmarshalJSON([]byte(out), &ko)
return ko.Address, ko.PubKey
address, err := sdk.GetAccAddressBech32(ko.Address)
require.NoError(t, err)
pk, err := sdk.GetAccPubKeyBech32(ko.PubKey)
require.NoError(t, err)
return address, pk
} }
func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount { func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
@ -204,11 +193,11 @@ func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
return acc return acc
} }
func executeGetCandidate(t *testing.T, cmdStr string) stake.Validator { func executeGetValidator(t *testing.T, cmdStr string) stake.Validator {
out := tests.ExecuteT(t, cmdStr) out := tests.ExecuteT(t, cmdStr)
var candidate stake.Validator var validator stake.Validator
cdc := app.MakeCodec() cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &candidate) err := cdc.UnmarshalJSON([]byte(out), &validator)
require.NoError(t, err, "out %v, err %v", out, err) require.NoError(t, err, "out %v\n, err %v", out, err)
return candidate return validator
} }

View File

@ -14,6 +14,7 @@ import (
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli"
slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
@ -35,36 +36,83 @@ func main() {
// the below functions and eliminate global vars, like we do // the below functions and eliminate global vars, like we do
// with the cdc // with the cdc
// add standard rpc, and tx commands // add standard rpc commands
rpc.AddCommands(rootCmd) rpc.AddCommands(rootCmd)
rootCmd.AddCommand(client.LineBreak)
tx.AddCommands(rootCmd, cdc)
rootCmd.AddCommand(client.LineBreak)
// add query/post commands (custom to binary) //Add state commands
tendermintCmd := &cobra.Command{
Use: "tendermint",
Short: "Tendermint state querying subcommands",
}
tendermintCmd.AddCommand(
rpc.BlockCommand(),
rpc.ValidatorCommand(),
)
tx.AddCommands(tendermintCmd, cdc)
//Add IBC commands
ibcCmd := &cobra.Command{
Use: "ibc",
Short: "Inter-Blockchain Communication subcommands",
}
ibcCmd.AddCommand(
client.PostCommands(
ibccmd.IBCTransferCmd(cdc),
ibccmd.IBCRelayCmd(cdc),
)...)
advancedCmd := &cobra.Command{
Use: "advanced",
Short: "Advanced subcommands",
}
advancedCmd.AddCommand(
tendermintCmd,
ibcCmd,
lcd.ServeCommand(cdc),
)
rootCmd.AddCommand( rootCmd.AddCommand(
advancedCmd,
client.LineBreak,
)
//Add stake commands
stakeCmd := &cobra.Command{
Use: "stake",
Short: "Stake and validation subcommands",
}
stakeCmd.AddCommand(
client.GetCommands( client.GetCommands(
authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)),
stakecmd.GetCmdQueryValidator("stake", cdc), stakecmd.GetCmdQueryValidator("stake", cdc),
stakecmd.GetCmdQueryValidators("stake", cdc), stakecmd.GetCmdQueryValidators("stake", cdc),
stakecmd.GetCmdQueryDelegation("stake", cdc), stakecmd.GetCmdQueryDelegation("stake", cdc),
stakecmd.GetCmdQueryDelegations("stake", cdc), stakecmd.GetCmdQueryDelegations("stake", cdc),
slashingcmd.GetCmdQuerySigningInfo("slashing", cdc),
)...)
stakeCmd.AddCommand(
client.PostCommands(
stakecmd.GetCmdCreateValidator(cdc),
stakecmd.GetCmdEditValidator(cdc),
stakecmd.GetCmdDelegate(cdc),
stakecmd.GetCmdUnbond(cdc),
slashingcmd.GetCmdUnrevoke(cdc),
)...)
rootCmd.AddCommand(
stakeCmd,
)
//Add auth and bank commands
rootCmd.AddCommand(
client.GetCommands(
authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)),
)...) )...)
rootCmd.AddCommand( rootCmd.AddCommand(
client.PostCommands( client.PostCommands(
bankcmd.SendTxCmd(cdc), bankcmd.SendTxCmd(cdc),
ibccmd.IBCTransferCmd(cdc),
ibccmd.IBCRelayCmd(cdc),
stakecmd.GetCmdDeclareCandidacy(cdc),
stakecmd.GetCmdEditCandidacy(cdc),
stakecmd.GetCmdDelegate(cdc),
stakecmd.GetCmdUnbond(cdc),
)...) )...)
// add proxy, version and key info // add proxy, version and key info
rootCmd.AddCommand( rootCmd.AddCommand(
client.LineBreak,
lcd.ServeCommand(cdc),
keys.Commands(), keys.Commands(),
client.LineBreak, client.LineBreak,
version.VersionCmd, version.VersionCmd,

View File

@ -6,6 +6,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/cli" "github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@ -17,6 +18,7 @@ import (
func main() { func main() {
cdc := app.MakeCodec() cdc := app.MakeCodec()
ctx := server.NewDefaultContext() ctx := server.NewDefaultContext()
cobra.EnableCommandSorting = false
rootCmd := &cobra.Command{ rootCmd := &cobra.Command{
Use: "gaiad", Use: "gaiad",
Short: "Gaia Daemon (server)", Short: "Gaia Daemon (server)",
@ -25,7 +27,7 @@ func main() {
server.AddCommands(ctx, cdc, rootCmd, app.GaiaAppInit(), server.AddCommands(ctx, cdc, rootCmd, app.GaiaAppInit(),
server.ConstructAppCreator(newApp, "gaia"), server.ConstructAppCreator(newApp, "gaia"),
server.ConstructAppExporter(exportAppState, "gaia")) server.ConstructAppExporter(exportAppStateAndTMValidators, "gaia"))
// prepare and add flags // prepare and add flags
executor := cli.PrepareBaseCmd(rootCmd, "GA", app.DefaultNodeHome) executor := cli.PrepareBaseCmd(rootCmd, "GA", app.DefaultNodeHome)
@ -36,7 +38,7 @@ func newApp(logger log.Logger, db dbm.DB) abci.Application {
return app.NewGaiaApp(logger, db) return app.NewGaiaApp(logger, db)
} }
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) { func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error) {
gapp := app.NewGaiaApp(logger, db) gapp := app.NewGaiaApp(logger, db)
return gapp.ExportAppStateJSON() return gapp.ExportAppStateAndValidators()
} }

View File

@ -2,7 +2,7 @@ swagger: '2.0'
info: info:
version: '1.1.0' version: '1.1.0'
title: Light client daemon to interface with Cosmos baseserver via REST title: Light client daemon to interface with Cosmos baseserver via REST
description: Specification for the LCD provided by `gaiacli rest-server` description: Specification for the LCD provided by `gaiacli advanced rest-server`
securityDefinitions: securityDefinitions:
@ -102,7 +102,7 @@ paths:
- application/json - application/json
responses: responses:
200: 200:
description: 12 word Seed description: 16 word Seed
schema: schema:
type: string type: string
/keys/{name}: /keys/{name}:
@ -204,7 +204,7 @@ paths:
parameters: parameters:
- in: path - in: path
name: address name: address
description: Account address description: Account address in bech32 format
required: true required: true
type: string type: string
get: get:
@ -222,7 +222,7 @@ paths:
parameters: parameters:
- in: path - in: path
name: address name: address
description: Account address description: Account address in bech32 format
required: true required: true
type: string type: string
post: post:
@ -255,18 +255,6 @@ paths:
description: Tx was send and will probably be added to the next block description: Tx was send and will probably be added to the next block
400: 400:
description: The Tx was malformated description: The Tx was malformated
/accounts/{address}/nonce:
parameters:
- in: path
name: address
description: Account address
required: true
type: string
get:
summary: Get the nonce for a certain account
responses:
200:
description: Plaintext nonce i.e. "4" defaults to "0"
/blocks/latest: /blocks/latest:
get: get:
summary: Get the latest block summary: Get the latest block
@ -304,9 +292,14 @@ paths:
200: 200:
description: The validator set at the latest block height description: The validator set at the latest block height
schema: schema:
type: object
properties:
block_height:
type: number
validators:
type: array type: array
items: items:
$ref: "#/definitions/Delegate" $ref: "#/definitions/Validator"
/validatorsets/{height}: /validatorsets/{height}:
parameters: parameters:
- in: path - in: path
@ -322,9 +315,14 @@ paths:
200: 200:
description: The validator set at a specific block height description: The validator set at a specific block height
schema: schema:
type: object
properties:
block_height:
type: number
validators:
type: array type: array
items: items:
$ref: "#/definitions/Delegate" $ref: "#/definitions/Validator"
404: 404:
description: Block at height not available description: Block at height not available
# /txs: # /txs:
@ -549,7 +547,20 @@ paths:
definitions: definitions:
Address: Address:
type: string type: string
example: DF096FDE8D380FA5B2AD20DB2962C82DDEA1ED9B description: bech32 encoded addres
example: cosmosaccaddr:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
ValidatorAddress:
type: string
description: bech32 encoded addres
example: cosmosvaladdr:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
PubKey:
type: string
description: bech32 encoded public key
example: cosmosaccpub:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
ValidatorPubKey:
type: string
description: bech32 encoded public key
example: cosmosvalpub:zgnkwr7eyyv643dllwfpdwensmgdtz89yu73zq
Coins: Coins:
type: object type: object
properties: properties:
@ -652,16 +663,6 @@ definitions:
example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958 example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958
Pubkey: Pubkey:
$ref: "#/definitions/PubKey" $ref: "#/definitions/PubKey"
PubKey:
type: object
properties:
type:
type: string
enum:
- ed25519
data:
type: string
example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958
Account: Account:
type: object type: object
properties: properties:
@ -753,17 +754,19 @@ definitions:
type: array type: array
items: items:
type: object type: object
Delegate: Validator:
type: object type: object
properties: properties:
address:
$ref: '#/definitions/ValidatorAddress'
pub_key: pub_key:
$ref: "#/definitions/PubKey" $ref: "#/definitions/ValidatorPubKey"
power: power:
type: number type: number
example: 1000 example: 1000
name: accum:
type: string type: number
example: "159.89.3.34" example: 1000
# Added by API Auto Mocking Plugin # Added by API Auto Mocking Plugin
host: virtserver.swaggerhub.com host: virtserver.swaggerhub.com
basePath: /faboweb1/Cosmos-LCD-2/1.0.0 basePath: /faboweb1/Cosmos-LCD-2/1.0.0

View File

@ -260,11 +260,11 @@ the first part will look like:
and you want the ``pub_key`` ``data`` that starts with ``96864CE``. and you want the ``pub_key`` ``data`` that starts with ``96864CE``.
Now ``bob`` can declare candidacy to that pubkey: Now ``bob`` can create a validator with that pubkey.
:: ::
gaiacli declare-candidacy --amount=10mycoin --name=bob --pubkey=<pub_key data> --moniker=bobby gaiacli stake create-validator --amount=10mycoin --name=bob --address-validator=<address> --pub-key=<pubkey> --moniker=bobby
with an output like: with an output like:
@ -306,13 +306,13 @@ First let's have ``alice`` send some coins to ``charlie``:
:: ::
gaiacli tx --amount=1000mycoin --sequence=2 --name=alice --to=48F74F48281C89E5E4BE9092F735EA519768E8EF gaiacli send --amount=1000mycoin --sequence=2 --name=alice --to=48F74F48281C89E5E4BE9092F735EA519768E8EF
Then ``charlie`` will delegate some mycoin to ``bob``: Then ``charlie`` will delegate some mycoin to ``bob``:
:: ::
gaiacli tx delegate --amount=10mycoin --name=charlie --pubkey=<pub_key data> gaiacli stake delegate --amount=10mycoin --address-delegator=<charlie's address> --address-validator=<bob's address> --name=charlie
You'll see output like: You'll see output like:
@ -334,7 +334,7 @@ To get more information about the candidate, try:
:: ::
gaiacli query candidate --pubkey=<pub_key data> gaiacli stake validator <address>
and you'll see output similar to: and you'll see output similar to:
@ -367,7 +367,7 @@ It's also possible the query the delegator's bond like so:
:: ::
gaiacli query delegator-bond --delegator-address 48F74F48281C89E5E4BE9092F735EA519768E8EF --pubkey 52D6FCD8C92A97F7CCB01205ADF310A18411EA8FDCC10E65BF2FCDB05AD1689B gaiacli stake delegation --address-delegator=<address> --address-validator=<address>
with an output similar to: with an output similar to:
@ -385,7 +385,7 @@ with an output similar to:
} }
where the ``--delegator-address`` is ``charlie``'s address and the ``-pubkey`` is the same as we've been using. where the ``--address-delegator`` is ``charlie``'s address and the ``--address-validator`` is ``bob``'s address.
Unbonding Unbonding
@ -396,7 +396,7 @@ your VotingPower reduce and your account balance increase.
:: ::
gaiacli unbond --amount=5mycoin --name=charlie --pubkey=<pub_key data> gaiacli stake unbond --amount=5mycoin --name=charlie --address-delegator=<address> --address-validator=<address>
gaiacli account 48F74F48281C89E5E4BE9092F735EA519768E8EF gaiacli account 48F74F48281C89E5E4BE9092F735EA519768E8EF
See the bond decrease with ``gaiacli query delegator-bond`` like above. See the bond decrease with ``gaiacli stake delegation`` like above.

View File

@ -44,7 +44,7 @@ Nice. We can also lookup the validator set:
:: ::
gaiacli validatorset gaiacli advanced tendermint validator-set
Then, we try to transfer some ``steak`` to another account: Then, we try to transfer some ``steak`` to another account:
@ -72,7 +72,7 @@ Finally, to relinquish all your power, unbond some coins. You should see your Vo
:: ::
gaiacli unbond --chain-id=<chain-id> --name=test gaiacli stake unbond --chain-id=<chain-id> --name=test
That's it! That's it!

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@ -14,6 +15,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc" "github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types" "github.com/cosmos/cosmos-sdk/examples/basecoin/types"
@ -33,6 +35,7 @@ type BasecoinApp struct {
keyAccount *sdk.KVStoreKey keyAccount *sdk.KVStoreKey
keyIBC *sdk.KVStoreKey keyIBC *sdk.KVStoreKey
keyStake *sdk.KVStoreKey keyStake *sdk.KVStoreKey
keySlashing *sdk.KVStoreKey
// Manage getting and setting accounts // Manage getting and setting accounts
accountMapper auth.AccountMapper accountMapper auth.AccountMapper
@ -40,6 +43,7 @@ type BasecoinApp struct {
coinKeeper bank.Keeper coinKeeper bank.Keeper
ibcMapper ibc.Mapper ibcMapper ibc.Mapper
stakeKeeper stake.Keeper stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
} }
func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
@ -55,6 +59,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
keyAccount: sdk.NewKVStoreKey("acc"), keyAccount: sdk.NewKVStoreKey("acc"),
keyIBC: sdk.NewKVStoreKey("ibc"), keyIBC: sdk.NewKVStoreKey("ibc"),
keyStake: sdk.NewKVStoreKey("stake"), keyStake: sdk.NewKVStoreKey("stake"),
keySlashing: sdk.NewKVStoreKey("slashing"),
} }
// Define the accountMapper. // Define the accountMapper.
@ -68,6 +73,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
app.coinKeeper = bank.NewKeeper(app.accountMapper) app.coinKeeper = bank.NewKeeper(app.accountMapper)
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace)) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace))
// register message routes // register message routes
app.Router(). app.Router().
@ -78,8 +84,10 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
// Initialize BaseApp. // Initialize BaseApp.
app.SetInitChainer(app.initChainer) app.SetInitChainer(app.initChainer)
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake) app.SetBeginBlocker(app.BeginBlocker)
app.SetEndBlocker(app.EndBlocker)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper)) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing)
err := app.LoadLatestVersion(app.keyMain) err := app.LoadLatestVersion(app.keyMain)
if err != nil { if err != nil {
cmn.Exit(err.Error()) cmn.Exit(err.Error())
@ -94,6 +102,7 @@ func MakeCodec() *wire.Codec {
sdk.RegisterWire(cdc) // Register Msgs sdk.RegisterWire(cdc) // Register Msgs
bank.RegisterWire(cdc) bank.RegisterWire(cdc)
stake.RegisterWire(cdc) stake.RegisterWire(cdc)
slashing.RegisterWire(cdc)
ibc.RegisterWire(cdc) ibc.RegisterWire(cdc)
// register custom AppAccount // register custom AppAccount
@ -102,6 +111,24 @@ func MakeCodec() *wire.Codec {
return cdc return cdc
} }
// application updates every end block
func (app *BasecoinApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper)
return abci.ResponseBeginBlock{
Tags: tags.ToKVPairs(),
}
}
// application updates every end block
func (app *BasecoinApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper)
return abci.ResponseEndBlock{
ValidatorUpdates: validatorUpdates,
}
}
// Custom logic for basecoin initialization // Custom logic for basecoin initialization
func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes stateJSON := req.AppStateBytes
@ -121,11 +148,15 @@ func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain)
} }
app.accountMapper.SetAccount(ctx, acc) app.accountMapper.SetAccount(ctx, acc)
} }
// load the initial stake information
stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
return abci.ResponseInitChain{} return abci.ResponseInitChain{}
} }
// Custom logic for state export // Custom logic for state export
func (app *BasecoinApp) ExportAppStateJSON() (appState json.RawMessage, err error) { func (app *BasecoinApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
ctx := app.NewContext(true, abci.Header{}) ctx := app.NewContext(true, abci.Header{})
// iterate to get the accounts // iterate to get the accounts
@ -143,5 +174,10 @@ func (app *BasecoinApp) ExportAppStateJSON() (appState json.RawMessage, err erro
genState := types.GenesisState{ genState := types.GenesisState{
Accounts: accounts, Accounts: accounts,
} }
return wire.MarshalJSONIndent(app.cdc, genState) appState, err = wire.MarshalJSONIndent(app.cdc, genState)
if err != nil {
return nil, nil, err
}
validators = stake.WriteValidators(ctx, app.stakeKeeper)
return appState, validators, err
} }

View File

@ -11,9 +11,11 @@ import (
"github.com/cosmos/cosmos-sdk/examples/basecoin/types" "github.com/cosmos/cosmos-sdk/examples/basecoin/types"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc" "github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/stake"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
@ -85,6 +87,30 @@ var (
} }
) )
func setGenesis(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,
StakeData: stake.DefaultGenesisState(),
}
stateBytes, err := wire.MarshalJSONIndent(bapp.cdc, genesisState)
if err != nil {
return err
}
// Initialize the chain
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit()
return nil
}
func loggerAndDB() (log.Logger, dbm.DB) { func loggerAndDB() (log.Logger, dbm.DB) {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
db := dbm.NewMemDB() db := dbm.NewMemDB()
@ -96,33 +122,11 @@ func newBasecoinApp() *BasecoinApp {
return NewBasecoinApp(logger, db) return NewBasecoinApp(logger, db)
} }
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
}
//_______________________________________________________________________ //_______________________________________________________________________
func TestMsgs(t *testing.T) { func TestMsgs(t *testing.T) {
bapp := newBasecoinApp() bapp := newBasecoinApp()
require.Nil(t, setGenesis(bapp))
msgs := []struct { msgs := []struct {
msg sdk.Msg msg sdk.Msg
@ -161,7 +165,7 @@ func TestSortGenesis(t *testing.T) {
// Initialize the chain // Initialize the chain
vals := []abci.Validator{} vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, []byte(genState)}) bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: []byte(genState)})
bapp.Commit() bapp.Commit()
// Unsorted coins means invalid // Unsorted coins means invalid
@ -193,8 +197,8 @@ func TestGenesis(t *testing.T) {
} }
acc := &types.AppAccount{baseAcc, "foobart"} acc := &types.AppAccount{baseAcc, "foobart"}
err = setGenesisAccounts(bapp, baseAcc) err = setGenesis(bapp, baseAcc)
assert.Nil(t, err) require.Nil(t, err)
// A checkTx context // A checkTx context
ctx := bapp.BaseApp.NewContext(true, abci.Header{}) ctx := bapp.BaseApp.NewContext(true, abci.Header{})
@ -222,8 +226,9 @@ func TestMsgChangePubKey(t *testing.T) {
} }
// Construct genesis state // Construct genesis state
err = setGenesisAccounts(bapp, baseAcc) err = setGenesis(bapp, baseAcc)
assert.Nil(t, err) require.Nil(t, err)
// A checkTx context (true) // A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{}) ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1) res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
@ -276,8 +281,9 @@ func TestMsgSendWithAccounts(t *testing.T) {
} }
// Construct genesis state // Construct genesis state
err = setGenesisAccounts(bapp, baseAcc) err = setGenesis(bapp, baseAcc)
assert.Nil(t, err) require.Nil(t, err)
// A checkTx context (true) // A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{}) ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1) res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
@ -320,8 +326,9 @@ func TestMsgSendMultipleOut(t *testing.T) {
Coins: genCoins, Coins: genCoins,
} }
err = setGenesisAccounts(bapp, acc1, acc2) // Construct genesis state
assert.Nil(t, err) err = setGenesis(bapp, acc1, acc2)
require.Nil(t, err)
// Simulate a Block // Simulate a Block
SignCheckDeliver(t, bapp, sendMsg2, []int64{0}, true, priv1) SignCheckDeliver(t, bapp, sendMsg2, []int64{0}, true, priv1)
@ -353,7 +360,7 @@ func TestSengMsgMultipleInOut(t *testing.T) {
Coins: genCoins, Coins: genCoins,
} }
err = setGenesisAccounts(bapp, acc1, acc2, acc4) err = setGenesis(bapp, acc1, acc2, acc4)
assert.Nil(t, err) assert.Nil(t, err)
// CheckDeliver // CheckDeliver
@ -377,7 +384,11 @@ func TestMsgSendDependent(t *testing.T) {
Coins: genCoins, Coins: genCoins,
} }
err = setGenesisAccounts(bapp, acc1) // Construct genesis state
err = setGenesis(bapp, acc1)
require.Nil(t, err)
err = setGenesis(bapp, acc1)
assert.Nil(t, err) assert.Nil(t, err)
// CheckDeliver // CheckDeliver
@ -416,7 +427,7 @@ func TestMsgQuiz(t *testing.T) {
// Initialize the chain (nil) // Initialize the chain (nil)
vals := []abci.Validator{} vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes}) bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit() bapp.Commit()
// A checkTx context (true) // A checkTx context (true)
@ -438,8 +449,9 @@ func TestIBCMsgs(t *testing.T) {
} }
acc1 := &types.AppAccount{baseAcc, "foobart"} acc1 := &types.AppAccount{baseAcc, "foobart"}
err := setGenesisAccounts(bapp, baseAcc) err := setGenesis(bapp, baseAcc)
assert.Nil(t, err) assert.Nil(t, err)
// A checkTx context (true) // A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{}) ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1) res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)

View File

@ -59,8 +59,8 @@ func main() {
bankcmd.SendTxCmd(cdc), bankcmd.SendTxCmd(cdc),
ibccmd.IBCTransferCmd(cdc), ibccmd.IBCTransferCmd(cdc),
ibccmd.IBCRelayCmd(cdc), ibccmd.IBCRelayCmd(cdc),
stakecmd.GetCmdDeclareCandidacy(cdc), stakecmd.GetCmdCreateValidator(cdc),
stakecmd.GetCmdEditCandidacy(cdc), stakecmd.GetCmdEditValidator(cdc),
stakecmd.GetCmdDelegate(cdc), stakecmd.GetCmdDelegate(cdc),
stakecmd.GetCmdUnbond(cdc), stakecmd.GetCmdUnbond(cdc),
)...) )...)

View File

@ -7,6 +7,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/cli" "github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@ -27,7 +28,7 @@ func main() {
server.AddCommands(ctx, cdc, rootCmd, server.DefaultAppInit, server.AddCommands(ctx, cdc, rootCmd, server.DefaultAppInit,
server.ConstructAppCreator(newApp, "basecoin"), server.ConstructAppCreator(newApp, "basecoin"),
server.ConstructAppExporter(exportAppState, "basecoin")) server.ConstructAppExporter(exportAppStateAndTMValidators, "basecoin"))
// prepare and add flags // prepare and add flags
rootDir := os.ExpandEnv("$HOME/.basecoind") rootDir := os.ExpandEnv("$HOME/.basecoind")
@ -39,7 +40,7 @@ func newApp(logger log.Logger, db dbm.DB) abci.Application {
return app.NewBasecoinApp(logger, db) return app.NewBasecoinApp(logger, db)
} }
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) { func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error) {
bapp := app.NewBasecoinApp(logger, db) bapp := app.NewBasecoinApp(logger, db)
return bapp.ExportAppStateJSON() return bapp.ExportAppStateAndValidators()
} }

View File

@ -4,6 +4,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/stake"
) )
var _ auth.Account = (*AppAccount)(nil) var _ auth.Account = (*AppAccount)(nil)
@ -42,6 +43,7 @@ func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder {
// State to Unmarshal // State to Unmarshal
type GenesisState struct { type GenesisState struct {
Accounts []*GenesisAccount `json:"accounts"` Accounts []*GenesisAccount `json:"accounts"`
StakeData stake.GenesisState `json:"stake"`
} }
// GenesisAccount doesn't need pubkey or sequence // GenesisAccount doesn't need pubkey or sequence

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@ -154,7 +155,7 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep
} }
// Custom logic for state export // Custom logic for state export
func (app *DemocoinApp) ExportAppStateJSON() (appState json.RawMessage, err error) { func (app *DemocoinApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
ctx := app.NewContext(true, abci.Header{}) ctx := app.NewContext(true, abci.Header{})
// iterate to get the accounts // iterate to get the accounts
@ -174,5 +175,9 @@ func (app *DemocoinApp) ExportAppStateJSON() (appState json.RawMessage, err erro
POWGenesis: pow.WriteGenesis(ctx, app.powKeeper), POWGenesis: pow.WriteGenesis(ctx, app.powKeeper),
CoolGenesis: cool.WriteGenesis(ctx, app.coolKeeper), CoolGenesis: cool.WriteGenesis(ctx, app.coolKeeper),
} }
return wire.MarshalJSONIndent(app.cdc, genState) appState, err = wire.MarshalJSONIndent(app.cdc, genState)
if err != nil {
return nil, nil, err
}
return appState, validators, nil
} }

View File

@ -142,7 +142,7 @@ func TestGenesis(t *testing.T) {
stateBytes, err := json.MarshalIndent(genesisState, "", "\t") stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
vals := []abci.Validator{} vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes}) bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit() bapp.Commit()
// A checkTx context // A checkTx context
@ -184,7 +184,7 @@ func TestMsgSendWithAccounts(t *testing.T) {
// Initialize the chain // Initialize the chain
vals := []abci.Validator{} vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes}) bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit() bapp.Commit()
// A checkTx context (true) // A checkTx context (true)
@ -262,7 +262,7 @@ func TestMsgMine(t *testing.T) {
// Initialize the chain (nil) // Initialize the chain (nil)
vals := []abci.Validator{} vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes}) bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit() bapp.Commit()
// A checkTx context (true) // A checkTx context (true)
@ -309,7 +309,7 @@ func TestMsgQuiz(t *testing.T) {
// Initialize the chain (nil) // Initialize the chain (nil)
vals := []abci.Validator{} vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes}) bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit() bapp.Commit()
// A checkTx context (true) // A checkTx context (true)
@ -356,7 +356,7 @@ func TestHandler(t *testing.T) {
} }
stateBytes, err := json.MarshalIndent(genesisState, "", "\t") stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err) require.Nil(t, err)
bapp.InitChain(abci.RequestInitChain{vals, stateBytes}) bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit() bapp.Commit()
// A checkTx context (true) // A checkTx context (true)

View File

@ -7,6 +7,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/cli" "github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@ -46,9 +47,9 @@ func newApp(logger log.Logger, db dbm.DB) abci.Application {
return app.NewDemocoinApp(logger, db) return app.NewDemocoinApp(logger, db)
} }
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) { func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error) {
dapp := app.NewDemocoinApp(logger, db) dapp := app.NewDemocoinApp(logger, db)
return dapp.ExportAppStateJSON() return dapp.ExportAppStateAndValidators()
} }
func main() { func main() {
@ -63,7 +64,7 @@ func main() {
server.AddCommands(ctx, cdc, rootCmd, CoolAppInit, server.AddCommands(ctx, cdc, rootCmd, CoolAppInit,
server.ConstructAppCreator(newApp, "democoin"), server.ConstructAppCreator(newApp, "democoin"),
server.ConstructAppExporter(exportAppState, "democoin")) server.ConstructAppExporter(exportAppStateAndTMValidators, "democoin"))
// prepare and add flags // prepare and add flags
rootDir := os.ExpandEnv("$HOME/.democoind") rootDir := os.ExpandEnv("$HOME/.democoind")

View File

@ -2,6 +2,7 @@ package simplestake
import ( import (
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
@ -27,7 +28,7 @@ func handleMsgBond(ctx sdk.Context, k Keeper, msg MsgBond) sdk.Result {
} }
valSet := abci.Validator{ valSet := abci.Validator{
PubKey: msg.PubKey.Bytes(), PubKey: tmtypes.TM2PB.PubKey(msg.PubKey),
Power: power, Power: power,
} }
@ -44,7 +45,7 @@ func handleMsgUnbond(ctx sdk.Context, k Keeper, msg MsgUnbond) sdk.Result {
} }
valSet := abci.Validator{ valSet := abci.Validator{
PubKey: pubKey.Bytes(), PubKey: tmtypes.TM2PB.PubKey(pubKey),
Power: int64(0), Power: int64(0),
} }

View File

@ -35,9 +35,9 @@ func TestKeeperGetSet(t *testing.T) {
cdc := wire.NewCodec() cdc := wire.NewCodec()
auth.RegisterBaseAccount(cdc) auth.RegisterBaseAccount(cdc)
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{}) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{})
stakeKeeper := NewKeeper(capKey, bank.NewKeeper(accountMapper), DefaultCodespace) stakeKeeper := NewKeeper(capKey, bank.NewKeeper(accountMapper), DefaultCodespace)
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
addr := sdk.Address([]byte("some-address")) addr := sdk.Address([]byte("some-address"))
bi := stakeKeeper.getBondInfo(ctx, addr) bi := stakeKeeper.getBondInfo(ctx, addr)

View File

@ -26,7 +26,7 @@ func NewMsgBond(addr sdk.Address, stake sdk.Coin, pubKey crypto.PubKey) MsgBond
} }
//nolint //nolint
func (msg MsgBond) Type() string { return moduleName } //TODO update "stake/declarecandidacy" func (msg MsgBond) Type() string { return moduleName } //TODO update "stake/createvalidator"
func (msg MsgBond) GetSigners() []sdk.Address { return []sdk.Address{msg.Address} } func (msg MsgBond) GetSigners() []sdk.Address { return []sdk.Address{msg.Address} }
// basic validation of the bond message // basic validation of the bond message
@ -65,7 +65,7 @@ func NewMsgUnbond(addr sdk.Address) MsgUnbond {
} }
//nolint //nolint
func (msg MsgUnbond) Type() string { return moduleName } //TODO update "stake/declarecandidacy" func (msg MsgUnbond) Type() string { return moduleName } //TODO update "stake/createvalidator"
func (msg MsgUnbond) GetSigners() []sdk.Address { return []sdk.Address{msg.Address} } func (msg MsgUnbond) GetSigners() []sdk.Address { return []sdk.Address{msg.Address} }
func (msg MsgUnbond) ValidateBasic() sdk.Error { return nil } func (msg MsgUnbond) ValidateBasic() sdk.Error { return nil }

View File

@ -5,6 +5,7 @@ import (
"path/filepath" "path/filepath"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
) )
@ -13,8 +14,8 @@ import (
// and other flags (?) to start // and other flags (?) to start
type AppCreator func(string, log.Logger) (abci.Application, error) type AppCreator func(string, log.Logger) (abci.Application, error)
// AppExporter dumps all app state to JSON-serializable structure // AppExporter dumps all app state to JSON-serializable structure and returns the current validator set
type AppExporter func(home string, log log.Logger) (json.RawMessage, error) type AppExporter func(home string, log log.Logger) (json.RawMessage, []tmtypes.GenesisValidator, error)
// ConstructAppCreator returns an application generation function // ConstructAppCreator returns an application generation function
func ConstructAppCreator(appFn func(log.Logger, dbm.DB) abci.Application, name string) AppCreator { func ConstructAppCreator(appFn func(log.Logger, dbm.DB) abci.Application, name string) AppCreator {
@ -30,12 +31,12 @@ func ConstructAppCreator(appFn func(log.Logger, dbm.DB) abci.Application, name s
} }
// ConstructAppExporter returns an application export function // ConstructAppExporter returns an application export function
func ConstructAppExporter(appFn func(log.Logger, dbm.DB) (json.RawMessage, error), name string) AppExporter { func ConstructAppExporter(appFn func(log.Logger, dbm.DB) (json.RawMessage, []tmtypes.GenesisValidator, error), name string) AppExporter {
return func(rootDir string, logger log.Logger) (json.RawMessage, error) { return func(rootDir string, logger log.Logger) (json.RawMessage, []tmtypes.GenesisValidator, error) {
dataDir := filepath.Join(rootDir, "data") dataDir := filepath.Join(rootDir, "data")
db, err := dbm.NewGoLevelDB(name, dataDir) db, err := dbm.NewGoLevelDB(name, dataDir)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
return appFn(logger, db) return appFn(logger, db)
} }

View File

@ -18,7 +18,7 @@ func ExportCmd(ctx *Context, cdc *wire.Codec, appExporter AppExporter) *cobra.Co
Short: "Export state to JSON", Short: "Export state to JSON",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
home := viper.GetString("home") home := viper.GetString("home")
appState, err := appExporter(home, ctx.Logger) appState, validators, err := appExporter(home, ctx.Logger)
if err != nil { if err != nil {
return errors.Errorf("Error exporting state: %v\n", err) return errors.Errorf("Error exporting state: %v\n", err)
} }
@ -27,6 +27,7 @@ func ExportCmd(ctx *Context, cdc *wire.Codec, appExporter AppExporter) *cobra.Co
return err return err
} }
doc.AppStateJSON = appState doc.AppStateJSON = appState
doc.Validators = validators
encoded, err := wire.MarshalJSONIndent(cdc, doc) encoded, err := wire.MarshalJSONIndent(cdc, doc)
if err != nil { if err != nil {
return err return err

View File

@ -37,7 +37,9 @@ func TestStartStandAlone(t *testing.T) {
app, err := mock.NewApp(home, logger) app, err := mock.NewApp(home, logger)
require.Nil(t, err) require.Nil(t, err)
svr, err := server.NewServer(FreeTCPAddr(t), "socket", app) svrAddr, _, err := FreeTCPAddr()
require.Nil(t, err)
svr, err := server.NewServer(svrAddr, "socket", app)
require.Nil(t, err, "Error creating listener") require.Nil(t, err, "Error creating listener")
svr.SetLogger(logger.With("module", "abci-server")) svr.SetLogger(logger.With("module", "abci-server"))
svr.Start() svr.Start()
@ -69,7 +71,9 @@ func TestStartWithTendermint(t *testing.T) {
// set up app and start up // set up app and start up
viper.Set(flagWithTendermint, true) viper.Set(flagWithTendermint, true)
startCmd := StartCmd(ctx, mock.NewApp) startCmd := StartCmd(ctx, mock.NewApp)
startCmd.Flags().Set(flagAddress, FreeTCPAddr(t)) // set to a new free address svrAddr, _, err := FreeTCPAddr()
require.NoError(t, err)
startCmd.Flags().Set(flagAddress, svrAddr) // set to a new free address
timeout := time.Duration(5) * time.Second timeout := time.Duration(5) * time.Second
close(RunOrTimeout(startCmd, timeout, t)) close(RunOrTimeout(startCmd, timeout, t))

View File

@ -16,14 +16,17 @@ import (
// Get a free address for a test tendermint server // Get a free address for a test tendermint server
// protocol is either tcp, http, etc // protocol is either tcp, http, etc
func FreeTCPAddr(t *testing.T) string { func FreeTCPAddr() (addr, port string, err error) {
l, err := net.Listen("tcp", "0.0.0.0:0") l, err := net.Listen("tcp", "0.0.0.0:0")
defer l.Close() defer l.Close()
require.Nil(t, err) if err != nil {
return "", "", err
}
port := l.Addr().(*net.TCPAddr).Port portI := l.Addr().(*net.TCPAddr).Port
addr := fmt.Sprintf("tcp://0.0.0.0:%d", port) port = fmt.Sprintf("%d", portI)
return addr addr = fmt.Sprintf("tcp://0.0.0.0:%s", port)
return
} }
// setupViper creates a homedir to run inside, // setupViper creates a homedir to run inside,

View File

@ -53,11 +53,11 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command {
fmt.Println(string(pubKeyJSONBytes)) fmt.Println(string(pubKeyJSONBytes))
return nil return nil
} }
addr, err := sdk.Bech32CosmosifyValPub(valPubKey) pubkey, err := sdk.Bech32ifyValPub(valPubKey)
if err != nil { if err != nil {
return err return err
} }
fmt.Println(addr) fmt.Println(pubkey)
return nil return nil
}, },
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
@ -72,13 +73,24 @@ func AddCommands(
rootCmd.PersistentFlags().String("log_level", ctx.Config.LogLevel, "Log level") rootCmd.PersistentFlags().String("log_level", ctx.Config.LogLevel, "Log level")
tendermintCmd := &cobra.Command{
Use: "tendermint",
Short: "Tendermint subcommands",
}
tendermintCmd.AddCommand(
ShowNodeIDCmd(ctx),
ShowValidatorCmd(ctx),
)
rootCmd.AddCommand( rootCmd.AddCommand(
InitCmd(ctx, cdc, appInit), InitCmd(ctx, cdc, appInit),
StartCmd(ctx, appCreator), StartCmd(ctx, appCreator),
UnsafeResetAllCmd(ctx), UnsafeResetAllCmd(ctx),
ShowNodeIDCmd(ctx), client.LineBreak,
ShowValidatorCmd(ctx), tendermintCmd,
ExportCmd(ctx, cdc, appExport), ExportCmd(ctx, cdc, appExport),
client.LineBreak,
version.VersionCmd, version.VersionCmd,
) )
} }

View File

@ -1,51 +1,91 @@
package tests package tests
import ( import (
"io"
"os/exec"
"strings" "strings"
"testing" "testing"
"time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
cmn "github.com/tendermint/tmlibs/common"
) )
func getCmd(t *testing.T, command string) *exec.Cmd { // Execute the command, return stdout, logging stdout/err to t.
func ExecuteT(t *testing.T, cmd string) (out string) {
t.Log("Running", cmn.Cyan(cmd))
//split command into command and args // Split cmd to name and args.
split := strings.Split(command, " ") split := strings.Split(cmd, " ")
require.True(t, len(split) > 0, "no command provided") require.True(t, len(split) > 0, "no command provided")
name, args := split[0], []string(nil)
var cmd *exec.Cmd if len(split) > 1 {
if len(split) == 1 { args = split[1:]
cmd = exec.Command(split[0])
} else {
cmd = exec.Command(split[0], split[1:]...)
} }
return cmd
}
// Execute the command, return standard output and error, try a few times if requested // Start process and wait.
func ExecuteT(t *testing.T, command string) (out string) { proc, err := StartProcess("", name, args, nil, nil)
cmd := getCmd(t, command) require.NoError(t, err)
bz, err := cmd.CombinedOutput() proc.Wait()
if err != nil {
panic(err) // Get the output.
outbz := proc.StdoutBuffer.Bytes()
errbz := proc.StderrBuffer.Bytes()
// Log output.
if len(outbz) > 0 {
t.Log("Stdout:", cmn.Green(string(outbz)))
} }
require.NoError(t, err, string(bz)) if len(errbz) > 0 {
out = strings.Trim(string(bz), "\n") //trim any new lines t.Log("Stderr:", cmn.Red(string(errbz)))
time.Sleep(time.Second) }
// Collect STDOUT output.
out = strings.Trim(string(outbz), "\n") //trim any new lines
return out return out
} }
// Asynchronously execute the command, return standard output and error // Execute the command, launch goroutines to log stdout/err to t.
func GoExecuteT(t *testing.T, command string) (cmd *exec.Cmd, pipeIn io.WriteCloser, pipeOut io.ReadCloser) { // Caller should wait for .Wait() or .Stop() to terminate.
cmd = getCmd(t, command) func GoExecuteT(t *testing.T, cmd string) (proc *Process) {
pipeIn, err := cmd.StdinPipe() t.Log("Running", cmn.Cyan(cmd))
// Split cmd to name and args.
split := strings.Split(cmd, " ")
require.True(t, len(split) > 0, "no command provided")
name, args := split[0], []string(nil)
if len(split) > 1 {
args = split[1:]
}
// Start process.
proc, err := StartProcess("", name, args, nil, nil)
require.NoError(t, err) require.NoError(t, err)
pipeOut, err = cmd.StdoutPipe()
require.NoError(t, err) // Run goroutines to log stdout.
cmd.Start() go func() {
time.Sleep(time.Second) buf := make([]byte, 10240) // TODO Document the effects.
return cmd, pipeIn, pipeOut for {
n, err := proc.StdoutBuffer.Read(buf)
if err != nil {
return
}
if n > 0 {
t.Log("Stdout:", cmn.Green(string(buf[:n])))
}
}
}()
// Run goroutines to log stderr.
go func() {
buf := make([]byte, 10240) // TODO Document the effects.
for {
n, err := proc.StderrBuffer.Read(buf)
if err != nil {
return
}
if n > 0 {
t.Log("Stderr:", cmn.Red(string(buf[:n])))
}
}
}()
return proc
} }

110
tests/process.go Normal file
View File

@ -0,0 +1,110 @@
package tests
import (
"bytes"
"io"
"os"
"os/exec"
"time"
)
// execution process
type Process struct {
ExecPath string
Args []string
Pid int
StartTime time.Time
EndTime time.Time
Cmd *exec.Cmd `json:"-"`
ExitState *os.ProcessState `json:"-"`
WaitCh chan struct{} `json:"-"`
StdinPipe io.WriteCloser `json:"-"`
StdoutBuffer *bytes.Buffer `json:"-"`
StderrBuffer *bytes.Buffer `json:"-"`
}
// dir: The working directory. If "", os.Getwd() is used.
// name: Command name
// args: Args to command. (should not include name)
// outFile, errFile: If not nil, will use, otherwise new Buffers will be
// allocated. Either way, Process.Cmd.StdoutPipe and Process.Cmd.StderrPipe will be nil
// respectively.
func StartProcess(dir string, name string, args []string, outFile, errFile io.WriteCloser) (*Process, error) {
var cmd = exec.Command(name, args...) // is not yet started.
// cmd dir
if dir == "" {
pwd, err := os.Getwd()
if err != nil {
panic(err)
}
cmd.Dir = pwd
} else {
cmd.Dir = dir
}
// cmd stdin
stdin, err := cmd.StdinPipe()
if err != nil {
return nil, err
}
// cmd stdout, stderr
var outBuffer, errBuffer *bytes.Buffer
if outFile != nil {
cmd.Stdout = outFile
} else {
outBuffer = bytes.NewBuffer(nil)
cmd.Stdout = outBuffer
}
if errFile != nil {
cmd.Stderr = errFile
} else {
errBuffer = bytes.NewBuffer(nil)
cmd.Stderr = errBuffer
}
// cmd start
if err := cmd.Start(); err != nil {
return nil, err
}
proc := &Process{
ExecPath: name,
Args: args,
Pid: cmd.Process.Pid,
StartTime: time.Now(),
Cmd: cmd,
ExitState: nil,
WaitCh: make(chan struct{}),
StdinPipe: stdin,
}
if outBuffer != nil {
proc.StdoutBuffer = outBuffer
}
if errBuffer != nil {
proc.StderrBuffer = errBuffer
}
go func() {
err := proc.Cmd.Wait()
if err != nil {
// fmt.Printf("Process exit: %v\n", err)
if exitError, ok := err.(*exec.ExitError); ok {
proc.ExitState = exitError.ProcessState
}
}
proc.ExitState = proc.Cmd.ProcessState
proc.EndTime = time.Now() // TODO make this goroutine-safe
close(proc.WaitCh)
}()
return proc, nil
}
// stop the process
func (proc *Process) Stop(kill bool) error {
if kill {
// fmt.Printf("Killing process %v\n", proc.Cmd.Process)
return proc.Cmd.Process.Kill()
}
return proc.Cmd.Process.Signal(os.Interrupt)
}
// wait for the process
func (proc *Process) Wait() {
<-proc.WaitCh
}

View File

@ -9,7 +9,6 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
//"strings"
"testing" "testing"
"time" "time"
@ -239,7 +238,9 @@ func StartNodeServerForTest(t *testing.T, home string) *exec.Cmd {
// expects TestInitBaseCoin to have been run // expects TestInitBaseCoin to have been run
func StartLCDServerForTest(t *testing.T, home, chainID string) (cmd *exec.Cmd, port string) { func StartLCDServerForTest(t *testing.T, home, chainID string) (cmd *exec.Cmd, port string) {
cmdName := whereIsBasecli() cmdName := whereIsBasecli()
port = strings.Split(server.FreeTCPAddr(t), ":")[2] var err error
_, port, err = server.FreeTCPAddr()
require.NoError(t, err)
cmdArgs := []string{ cmdArgs := []string{
"rest-server", "rest-server",
"--home", "--home",
@ -252,7 +253,7 @@ func StartLCDServerForTest(t *testing.T, home, chainID string) (cmd *exec.Cmd, p
cmd = exec.Command(cmdName, cmdArgs...) cmd = exec.Command(cmdName, cmdArgs...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
err := cmd.Start() err = cmd.Start()
require.Nil(t, err) require.Nil(t, err)
time.Sleep(time.Second * 2) // TODO: LOL time.Sleep(time.Second * 2) // TODO: LOL
return cmd, port return cmd, port

View File

@ -11,16 +11,22 @@ import (
rpcclient "github.com/tendermint/tendermint/rpc/lib/client" rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
) )
// TODO: these functions just print to Stdout.
// consider using the logger.
// Uses localhost // Uses localhost
func WaitForHeight(height int64, port string) { func WaitForHeight(height int64, port string) {
for { for {
var resultBlock ctypes.ResultBlock
url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest") url := fmt.Sprintf("http://localhost:%v/blocks/latest", port)
res, err := http.Get(url)
// get url, try a few times
var res *http.Response
var err error
for i := 0; i < 5; i++ {
res, err = http.Get(url)
if err == nil {
break
}
time.Sleep(time.Second)
}
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -31,6 +37,7 @@ func WaitForHeight(height int64, port string) {
} }
res.Body.Close() res.Body.Close()
var resultBlock ctypes.ResultBlock
err = cdc.UnmarshalJSON([]byte(body), &resultBlock) err = cdc.UnmarshalJSON([]byte(body), &resultBlock)
if err != nil { if err != nil {
fmt.Println("RES", res) fmt.Println("RES", res)
@ -45,45 +52,35 @@ func WaitForHeight(height int64, port string) {
} }
} }
// wait for 2 blocks. // wait for tendermint to start
// uses localhost
func WaitForStart(port string) { func WaitForStart(port string) {
waitHeight := int64(2) var err error
for { for i := 0; i < 5; i++ {
time.Sleep(time.Second) time.Sleep(time.Second)
url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest") url := fmt.Sprintf("http://localhost:%v/blocks/latest", port)
res, err := http.Get(url)
if err != nil { // get url, try a few times
panic(err) var res *http.Response
res, err = http.Get(url)
if err == nil || res == nil {
continue
} }
// waiting for server to start ... // waiting for server to start ...
if res.StatusCode != http.StatusOK { if res.StatusCode != http.StatusOK {
res.Body.Close() res.Body.Close()
continue
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
res.Body.Close()
resultBlock := new(ctypes.ResultBlock)
err = cdc.UnmarshalJSON([]byte(body), &resultBlock)
if err != nil {
fmt.Println("RES", res)
fmt.Println("BODY", string(body))
panic(err)
}
if resultBlock.Block.Height >= waitHeight {
return return
} }
} }
if err != nil {
panic(err)
}
} }
// TODO: these functions just print to Stdout.
// consider using the logger.
// Wait for the RPC server to respond to /status // Wait for the RPC server to respond to /status
func WaitForRPC(laddr string) { func WaitForRPC(laddr string) {
fmt.Println("LADDR", laddr) fmt.Println("LADDR", laddr)

View File

@ -5,8 +5,8 @@ import (
"errors" "errors"
"fmt" "fmt"
bech32cosmos "github.com/cosmos/bech32cosmos/go"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tmlibs/bech32"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
@ -21,24 +21,24 @@ const (
Bech32PrefixValPub = "cosmosvalpub" Bech32PrefixValPub = "cosmosvalpub"
) )
// Bech32CosmosifyAcc takes Address and returns the Bech32Cosmos encoded string // Bech32ifyAcc takes Address and returns the bech32 encoded string
func Bech32CosmosifyAcc(addr Address) (string, error) { func Bech32ifyAcc(addr Address) (string, error) {
return bech32cosmos.ConvertAndEncode(Bech32PrefixAccAddr, addr.Bytes()) return bech32.ConvertAndEncode(Bech32PrefixAccAddr, addr.Bytes())
} }
// Bech32CosmosifyAccPub takes AccountPubKey and returns the Bech32Cosmos encoded string // Bech32ifyAccPub takes AccountPubKey and returns the bech32 encoded string
func Bech32CosmosifyAccPub(pub crypto.PubKey) (string, error) { func Bech32ifyAccPub(pub crypto.PubKey) (string, error) {
return bech32cosmos.ConvertAndEncode(Bech32PrefixAccPub, pub.Bytes()) return bech32.ConvertAndEncode(Bech32PrefixAccPub, pub.Bytes())
} }
// Bech32CosmosifyVal returns the Bech32Cosmos encoded string for a validator address // Bech32ifyVal returns the bech32 encoded string for a validator address
func Bech32CosmosifyVal(addr Address) (string, error) { func Bech32ifyVal(addr Address) (string, error) {
return bech32cosmos.ConvertAndEncode(Bech32PrefixValAddr, addr.Bytes()) return bech32.ConvertAndEncode(Bech32PrefixValAddr, addr.Bytes())
} }
// Bech32CosmosifyValPub returns the Bech32Cosmos encoded string for a validator pubkey // Bech32ifyValPub returns the bech32 encoded string for a validator pubkey
func Bech32CosmosifyValPub(pub crypto.PubKey) (string, error) { func Bech32ifyValPub(pub crypto.PubKey) (string, error) {
return bech32cosmos.ConvertAndEncode(Bech32PrefixValPub, pub.Bytes()) return bech32.ConvertAndEncode(Bech32PrefixValPub, pub.Bytes())
} }
// create an Address from a string // create an Address from a string
@ -54,14 +54,29 @@ func GetAccAddressHex(address string) (addr Address, err error) {
} }
// create an Address from a string // create an Address from a string
func GetAccAddressBech32Cosmos(address string) (addr Address, err error) { func GetAccAddressBech32(address string) (addr Address, err error) {
bz, err := getFromBech32Cosmos(address, Bech32PrefixAccAddr) bz, err := getFromBech32(address, Bech32PrefixAccAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return Address(bz), nil return Address(bz), nil
} }
// create a Pubkey from a string
func GetAccPubKeyBech32(address string) (pk crypto.PubKey, err error) {
bz, err := getFromBech32(address, Bech32PrefixAccPub)
if err != nil {
return nil, err
}
pk, err = crypto.PubKeyFromBytes(bz)
if err != nil {
return nil, err
}
return pk, nil
}
// create an Address from a hex string // create an Address from a hex string
func GetValAddressHex(address string) (addr Address, err error) { func GetValAddressHex(address string) (addr Address, err error) {
if len(address) == 0 { if len(address) == 0 {
@ -74,9 +89,9 @@ func GetValAddressHex(address string) (addr Address, err error) {
return Address(bz), nil return Address(bz), nil
} }
// create an Address from a bech32cosmos string // create an Address from a bech32 string
func GetValAddressBech32Cosmos(address string) (addr Address, err error) { func GetValAddressBech32(address string) (addr Address, err error) {
bz, err := getFromBech32Cosmos(address, Bech32PrefixValAddr) bz, err := getFromBech32(address, Bech32PrefixValAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -84,8 +99,8 @@ func GetValAddressBech32Cosmos(address string) (addr Address, err error) {
} }
//Decode a validator publickey into a public key //Decode a validator publickey into a public key
func GetValPubKeyBech32Cosmos(pubkey string) (pk crypto.PubKey, err error) { func GetValPubKeyBech32(pubkey string) (pk crypto.PubKey, err error) {
bz, err := getFromBech32Cosmos(pubkey, Bech32PrefixValPub) bz, err := getFromBech32(pubkey, Bech32PrefixValPub)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -98,11 +113,11 @@ func GetValPubKeyBech32Cosmos(pubkey string) (pk crypto.PubKey, err error) {
return pk, nil return pk, nil
} }
func getFromBech32Cosmos(bech32, prefix string) ([]byte, error) { func getFromBech32(bech32str, prefix string) ([]byte, error) {
if len(bech32) == 0 { if len(bech32str) == 0 {
return nil, errors.New("must provide non-empty string") return nil, errors.New("must provide non-empty string")
} }
hrp, bz, err := bech32cosmos.DecodeAndConvert(bech32) hrp, bz, err := bech32.DecodeAndConvert(bech32str)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -31,6 +31,7 @@ type Context struct {
// create a new context // create a new context
func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byte, logger log.Logger) Context { func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byte, logger log.Logger) Context {
c := Context{ c := Context{
Context: context.Background(), Context: context.Background(),
pst: newThePast(), pst: newThePast(),
@ -43,6 +44,7 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, txBytes []byt
c = c.WithIsCheckTx(isCheckTx) c = c.WithIsCheckTx(isCheckTx)
c = c.WithTxBytes(txBytes) c = c.WithTxBytes(txBytes)
c = c.WithLogger(logger) c = c.WithLogger(logger)
c = c.WithSigningValidators(nil)
c = c.WithGasMeter(NewInfiniteGasMeter()) c = c.WithGasMeter(NewInfiniteGasMeter())
return c return c
} }
@ -128,6 +130,7 @@ const (
contextKeyIsCheckTx contextKeyIsCheckTx
contextKeyTxBytes contextKeyTxBytes
contextKeyLogger contextKeyLogger
contextKeySigningValidators
contextKeyGasMeter contextKeyGasMeter
) )
@ -157,6 +160,9 @@ func (c Context) TxBytes() []byte {
func (c Context) Logger() log.Logger { func (c Context) Logger() log.Logger {
return c.Value(contextKeyLogger).(log.Logger) return c.Value(contextKeyLogger).(log.Logger)
} }
func (c Context) SigningValidators() []abci.SigningValidator {
return c.Value(contextKeySigningValidators).([]abci.SigningValidator)
}
func (c Context) GasMeter() GasMeter { func (c Context) GasMeter() GasMeter {
return c.Value(contextKeyGasMeter).(GasMeter) return c.Value(contextKeyGasMeter).(GasMeter)
} }
@ -182,6 +188,9 @@ func (c Context) WithTxBytes(txBytes []byte) Context {
func (c Context) WithLogger(logger log.Logger) Context { func (c Context) WithLogger(logger log.Logger) Context {
return c.withValue(contextKeyLogger, logger) return c.withValue(contextKeyLogger, logger)
} }
func (c Context) WithSigningValidators(SigningValidators []abci.SigningValidator) Context {
return c.withValue(contextKeySigningValidators, SigningValidators)
}
func (c Context) WithGasMeter(meter GasMeter) Context { func (c Context) WithGasMeter(meter GasMeter) Context {
return c.withValue(contextKeyGasMeter, meter) return c.withValue(contextKeyGasMeter, meter)
} }

View File

@ -3,6 +3,7 @@ package types
import ( import (
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-crypto" "github.com/tendermint/go-crypto"
tmtypes "github.com/tendermint/tendermint/types"
) )
// status of a validator // status of a validator
@ -31,6 +32,7 @@ func BondStatusToString(b BondStatus) string {
// validator for a delegated proof of stake system // validator for a delegated proof of stake system
type Validator interface { type Validator interface {
GetMoniker() string // moniker of the validator
GetStatus() BondStatus // status of the validator GetStatus() BondStatus // status of the validator
GetOwner() Address // owner address to receive/return validators coins GetOwner() Address // owner address to receive/return validators coins
GetPubKey() crypto.PubKey // validation pubkey GetPubKey() crypto.PubKey // validation pubkey
@ -41,7 +43,7 @@ type Validator interface {
// validator which fulfills abci validator interface for use in Tendermint // validator which fulfills abci validator interface for use in Tendermint
func ABCIValidator(v Validator) abci.Validator { func ABCIValidator(v Validator) abci.Validator {
return abci.Validator{ return abci.Validator{
PubKey: v.GetPubKey().Bytes(), PubKey: tmtypes.TM2PB.PubKey(v.GetPubKey()),
Power: v.GetPower().Evaluate(), Power: v.GetPower().Evaluate(),
} }
} }
@ -58,6 +60,9 @@ type ValidatorSet interface {
Validator(Context, Address) Validator // get a particular validator by owner address Validator(Context, Address) Validator // get a particular validator by owner address
TotalPower(Context) Rat // total power of the validator set TotalPower(Context) Rat // total power of the validator set
Slash(Context, crypto.PubKey, int64, Rat) // slash the validator and delegators of the validator, specifying offence height & slash fraction
Revoke(Context, crypto.PubKey) // revoke a validator
Unrevoke(Context, crypto.PubKey) // unrevoke a validator
} }
//_______________________________________________________________________________ //_______________________________________________________________________________

View File

@ -25,6 +25,11 @@ func (t Tags) AppendTags(a Tags) Tags {
return append(t, a...) return append(t, a...)
} }
// Turn tags into KVPair list
func (t Tags) ToKVPairs() []cmn.KVPair {
return []cmn.KVPair(t)
}
// New variadic tags, must be k string, v []byte repeating // New variadic tags, must be k string, v []byte repeating
func NewTags(tags ...interface{}) Tags { func NewTags(tags ...interface{}) Tags {
var ret Tags var ret Tags

View File

@ -35,7 +35,7 @@ type Tx interface {
//__________________________________________________________ //__________________________________________________________
// TxDeocder unmarshals transaction bytes // TxDecoder unmarshals transaction bytes
type TxDecoder func(txBytes []byte) (Tx, Error) type TxDecoder func(txBytes []byte) (Tx, Error)
//__________________________________________________________ //__________________________________________________________

View File

@ -40,7 +40,7 @@ func GetAccountCmd(storeName string, cdc *wire.Codec, decoder auth.AccountDecode
// find the key to look up the account // find the key to look up the account
addr := args[0] addr := args[0]
key, err := sdk.GetAccAddressBech32Cosmos(addr) key, err := sdk.GetAccAddressBech32(addr)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,7 +1,6 @@
package rest package rest
import ( import (
"encoding/hex"
"fmt" "fmt"
"net/http" "net/http"
@ -26,17 +25,16 @@ func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, sto
func QueryAccountRequestHandlerFn(storeName string, cdc *wire.Codec, decoder auth.AccountDecoder, ctx context.CoreContext) http.HandlerFunc { func QueryAccountRequestHandlerFn(storeName string, cdc *wire.Codec, decoder auth.AccountDecoder, ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
addr := vars["address"] bech32addr := vars["address"]
bz, err := hex.DecodeString(addr) addr, err := sdk.GetAccAddressBech32(bech32addr)
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
return return
} }
key := sdk.Address(bz)
res, err := ctx.Query(key, storeName) res, err := ctx.Query(addr, storeName)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error()))) w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error())))

View File

@ -1,8 +1,6 @@
package auth package auth
import ( import (
"encoding/json"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
) )
@ -30,7 +28,7 @@ func (msg MsgChangeKey) ValidateBasic() sdk.Error {
// Implements Msg. // Implements Msg.
func (msg MsgChangeKey) GetSignBytes() []byte { func (msg MsgChangeKey) GetSignBytes() []byte {
b, err := json.Marshal(msg) // XXX: ensure some canonical form b, err := msgCdc.MarshalJSON(msg) // XXX: ensure some canonical form
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -1,8 +1,6 @@
package auth package auth
import ( import (
"encoding/json"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
) )
@ -70,7 +68,7 @@ func (fee StdFee) Bytes() []byte {
if len(fee.Amount) == 0 { if len(fee.Amount) == 0 {
fee.Amount = sdk.Coins{} fee.Amount = sdk.Coins{}
} }
bz, err := json.Marshal(fee) // TODO bz, err := msgCdc.MarshalJSON(fee) // TODO
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -95,7 +93,7 @@ type StdSignDoc struct {
// StdSignBytes returns the bytes to sign for a transaction. // StdSignBytes returns the bytes to sign for a transaction.
// TODO: change the API to just take a chainID and StdTx ? // TODO: change the API to just take a chainID and StdTx ?
func StdSignBytes(chainID string, sequences []int64, fee StdFee, msg sdk.Msg) []byte { func StdSignBytes(chainID string, sequences []int64, fee StdFee, msg sdk.Msg) []byte {
bz, err := json.Marshal(StdSignDoc{ bz, err := msgCdc.MarshalJSON(StdSignDoc{
ChainID: chainID, ChainID: chainID,
Sequences: sequences, Sequences: sequences,
FeeBytes: fee.Bytes(), FeeBytes: fee.Bytes(),

View File

@ -10,3 +10,10 @@ func RegisterWire(cdc *wire.Codec) {
cdc.RegisterConcrete(&BaseAccount{}, "auth/Account", nil) cdc.RegisterConcrete(&BaseAccount{}, "auth/Account", nil)
cdc.RegisterConcrete(MsgChangeKey{}, "auth/ChangeKey", nil) cdc.RegisterConcrete(MsgChangeKey{}, "auth/ChangeKey", nil)
} }
var msgCdc = wire.NewCodec()
func init() {
RegisterWire(msgCdc)
wire.RegisterCrypto(msgCdc)
}

View File

@ -34,7 +34,7 @@ func SendTxCmd(cdc *wire.Codec) *cobra.Command {
toStr := viper.GetString(flagTo) toStr := viper.GetString(flagTo)
to, err := sdk.GetAccAddressBech32Cosmos(toStr) to, err := sdk.GetAccAddressBech32(toStr)
if err != nil { if err != nil {
return err return err
} }

View File

@ -11,6 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/bank/client" "github.com/cosmos/cosmos-sdk/x/bank/client"
) )
@ -29,12 +30,25 @@ type sendBody struct {
Sequence int64 `json:"sequence"` Sequence int64 `json:"sequence"`
} }
var msgCdc = wire.NewCodec()
func init() {
bank.RegisterWire(msgCdc)
}
// SendRequestHandlerFn - http request handler to send coins to a address // SendRequestHandlerFn - http request handler to send coins to a address
func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc { func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
// collect data // collect data
vars := mux.Vars(r) vars := mux.Vars(r)
address := vars["address"] bech32addr := vars["address"]
address, err := sdk.GetAccAddressBech32(bech32addr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
var m sendBody var m sendBody
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
@ -43,7 +57,7 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreCont
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
return return
} }
err = json.Unmarshal(body, &m) err = msgCdc.UnmarshalJSON(body, &m)
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
@ -57,7 +71,7 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreCont
return return
} }
to, err := sdk.GetAccAddressHex(address) to, err := sdk.GetAccAddressHex(address.String())
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))

View File

@ -1,8 +1,6 @@
package bank package bank
import ( import (
"encoding/json"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
@ -55,7 +53,7 @@ func (msg MsgSend) ValidateBasic() sdk.Error {
// Implements Msg. // Implements Msg.
func (msg MsgSend) GetSignBytes() []byte { func (msg MsgSend) GetSignBytes() []byte {
b, err := json.Marshal(msg) // XXX: ensure some canonical form b, err := msgCdc.MarshalJSON(msg) // XXX: ensure some canonical form
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -104,7 +102,7 @@ func (msg MsgIssue) ValidateBasic() sdk.Error {
// Implements Msg. // Implements Msg.
func (msg MsgIssue) GetSignBytes() []byte { func (msg MsgIssue) GetSignBytes() []byte {
b, err := json.Marshal(msg) // XXX: ensure some canonical form b, err := msgCdc.MarshalJSON(msg) // XXX: ensure some canonical form
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -186,8 +186,14 @@ func TestMsgSendGetSignBytes(t *testing.T) {
Outputs: []Output{NewOutput(addr2, coins)}, Outputs: []Output{NewOutput(addr2, coins)},
} }
res := msg.GetSignBytes() res := msg.GetSignBytes()
unmarshaledMsg := &MsgSend{}
msgCdc.UnmarshalJSON(res, unmarshaledMsg)
assert.Equal(t, &msg, unmarshaledMsg)
// TODO bad results // TODO bad results
assert.Equal(t, string(res), `{"inputs":[{"address":"696E707574","coins":[{"denom":"atom","amount":10}]}],"outputs":[{"address":"6F7574707574","coins":[{"denom":"atom","amount":10}]}]}`) expected := `{"type":"EAFDE32A2C87F8","value":{"inputs":[{"address":"696E707574","coins":[{"denom":"atom","amount":10}]}],"outputs":[{"address":"6F7574707574","coins":[{"denom":"atom","amount":10}]}]}}`
assert.Equal(t, expected, string(res))
} }
func TestMsgSendGetSigners(t *testing.T) { func TestMsgSendGetSigners(t *testing.T) {
@ -255,8 +261,14 @@ func TestMsgIssueGetSignBytes(t *testing.T) {
Outputs: []Output{NewOutput(addr, coins)}, Outputs: []Output{NewOutput(addr, coins)},
} }
res := msg.GetSignBytes() res := msg.GetSignBytes()
unmarshaledMsg := &MsgIssue{}
msgCdc.UnmarshalJSON(res, unmarshaledMsg)
assert.Equal(t, &msg, unmarshaledMsg)
// TODO bad results // TODO bad results
assert.Equal(t, string(res), `{"banker":"696E707574","outputs":[{"address":"6C6F616E2D66726F6D2D62616E6B","coins":[{"denom":"atom","amount":10}]}]}`) expected := `{"type":"72E617C06ABAD0","value":{"banker":"696E707574","outputs":[{"address":"6C6F616E2D66726F6D2D62616E6B","coins":[{"denom":"atom","amount":10}]}]}}`
assert.Equal(t, expected, string(res))
} }
func TestMsgIssueGetSigners(t *testing.T) { func TestMsgIssueGetSigners(t *testing.T) {

View File

@ -9,3 +9,9 @@ func RegisterWire(cdc *wire.Codec) {
cdc.RegisterConcrete(MsgSend{}, "cosmos-sdk/Send", nil) cdc.RegisterConcrete(MsgSend{}, "cosmos-sdk/Send", nil)
cdc.RegisterConcrete(MsgIssue{}, "cosmos-sdk/Issue", nil) cdc.RegisterConcrete(MsgIssue{}, "cosmos-sdk/Issue", nil)
} }
var msgCdc = wire.NewCodec()
func init() {
RegisterWire(msgCdc)
}

View File

@ -35,7 +35,14 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.Core
// collect data // collect data
vars := mux.Vars(r) vars := mux.Vars(r)
destChainID := vars["destchain"] destChainID := vars["destchain"]
address := vars["address"] bech32addr := vars["address"]
address, err := sdk.GetAccAddressBech32(bech32addr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
var m transferBody var m transferBody
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
@ -58,7 +65,7 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.Core
return return
} }
bz, err := hex.DecodeString(address) bz, err := hex.DecodeString(address.String())
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))

View File

@ -0,0 +1,6 @@
package cli
// nolint
const (
FlagAddressValidator = "address-validator"
)

View File

@ -0,0 +1,57 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire" // XXX fix
"github.com/cosmos/cosmos-sdk/x/slashing"
)
// get the command to query signing info
func GetCmdQuerySigningInfo(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "signing-info [validator-pubkey]",
Short: "Query a validator's signing information",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
pk, err := sdk.GetValPubKeyBech32(args[0])
if err != nil {
return err
}
key := slashing.GetValidatorSigningInfoKey(pk.Address())
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(key, storeName)
if err != nil {
return err
}
signingInfo := new(slashing.ValidatorSigningInfo)
cdc.MustUnmarshalBinary(res, signingInfo)
switch viper.Get(cli.OutputFlag) {
case "text":
human := signingInfo.HumanReadableString()
fmt.Println(human)
case "json":
// parse out the signing info
output, err := wire.MarshalJSONIndent(cdc, signingInfo)
if err != nil {
return err
}
fmt.Println(string(output))
}
return nil
},
}
return cmd
}

View File

@ -0,0 +1,42 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/cosmos/cosmos-sdk/x/slashing"
)
// create unrevoke command
func GetCmdUnrevoke(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unrevoke",
Args: cobra.ExactArgs(1),
Short: "unrevoke validator previously revoked for downtime",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
validatorAddr, err := sdk.GetAccAddressBech32(args[0])
if err != nil {
return err
}
msg := slashing.NewMsgUnrevoke(validatorAddr)
// build and sign the transaction, then broadcast to Tendermint
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, msg, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
return cmd
}

52
x/slashing/errors.go Normal file
View File

@ -0,0 +1,52 @@
//nolint
package slashing
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Local code type
type CodeType = sdk.CodeType
const (
// Default slashing codespace
DefaultCodespace sdk.CodespaceType = 10
// Invalid validator
CodeInvalidValidator CodeType = 201
// Validator jailed
CodeValidatorJailed CodeType = 202
)
func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "That address is not associated with any known validator")
}
func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Validator does not exist for that address")
}
func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeValidatorJailed, "Validator jailed, cannot yet be unrevoked")
}
func codeToDefaultMsg(code CodeType) string {
switch code {
case CodeInvalidValidator:
return "Invalid Validator"
case CodeValidatorJailed:
return "Validator Jailed"
default:
return sdk.CodeToDefaultMsg(code)
}
}
func msgOrDefaultMsg(msg string, code CodeType) string {
if msg != "" {
return msg
}
return codeToDefaultMsg(code)
}
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
msg = msgOrDefaultMsg(msg, code)
return sdk.NewError(codespace, code, msg)
}

58
x/slashing/handler.go Normal file
View File

@ -0,0 +1,58 @@
package slashing
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
// NOTE msg already has validate basic run
switch msg := msg.(type) {
case MsgUnrevoke:
return handleMsgUnrevoke(ctx, msg, k)
default:
return sdk.ErrTxDecode("invalid message parse in staking module").Result()
}
}
}
// Validators must submit a transaction to unrevoke itself after
// having been revoked (and thus unbonded) for downtime
func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result {
// Validator must exist
validator := k.validatorSet.Validator(ctx, msg.ValidatorAddr)
if validator == nil {
return ErrNoValidatorForAddress(k.codespace).Result()
}
addr := validator.GetPubKey().Address()
// Signing info must exist
info, found := k.getValidatorSigningInfo(ctx, addr)
if !found {
return ErrNoValidatorForAddress(k.codespace).Result()
}
// Cannot be unrevoked until out of jail
if ctx.BlockHeader().Time < info.JailedUntil {
return ErrValidatorJailed(k.codespace).Result()
}
if ctx.IsCheckTx() {
return sdk.Result{}
}
// Update the starting height (so the validator can't be immediately revoked again)
info.StartHeight = ctx.BlockHeight()
k.setValidatorSigningInfo(ctx, addr, info)
// Unrevoke the validator
k.validatorSet.Unrevoke(ctx, validator.GetPubKey())
tags := sdk.NewTags("action", []byte("unrevoke"), "validator", msg.ValidatorAddr.Bytes())
return sdk.Result{
Tags: tags,
}
}

90
x/slashing/keeper.go Normal file
View File

@ -0,0 +1,90 @@
package slashing
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
crypto "github.com/tendermint/go-crypto"
)
// Keeper of the slashing store
type Keeper struct {
storeKey sdk.StoreKey
cdc *wire.Codec
validatorSet sdk.ValidatorSet
// codespace
codespace sdk.CodespaceType
}
// NewKeeper creates a slashing keeper
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, codespace sdk.CodespaceType) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
validatorSet: vs,
codespace: codespace,
}
return keeper
}
// handle a validator signing two blocks at the same height
func (k Keeper) handleDoubleSign(ctx sdk.Context, height int64, timestamp int64, pubkey crypto.PubKey) {
logger := ctx.Logger().With("module", "x/slashing")
age := ctx.BlockHeader().Time - timestamp
// Double sign too old
if age > MaxEvidenceAge {
logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, age of %d past max age of %d", pubkey.Address(), height, age, MaxEvidenceAge))
return
}
// Double sign confirmed
logger.Info(fmt.Sprintf("Confirmed double sign from %s at height %d, age of %d less than max age of %d", pubkey.Address(), height, age, MaxEvidenceAge))
k.validatorSet.Slash(ctx, pubkey, height, SlashFractionDoubleSign)
}
// handle a validator signature, must be called once per validator per block
func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey, signed bool) {
logger := ctx.Logger().With("module", "x/slashing")
height := ctx.BlockHeight()
if !signed {
logger.Info(fmt.Sprintf("Absent validator %s at height %d", pubkey.Address(), height))
}
address := pubkey.Address()
// Local index, so counts blocks validator *should* have signed
// Will use the 0-value default signing info if not present
signInfo, _ := k.getValidatorSigningInfo(ctx, address)
index := signInfo.IndexOffset % SignedBlocksWindow
signInfo.IndexOffset++
// Update signed block bit array & counter
// This counter just tracks the sum of the bit array
// That way we avoid needing to read/write the whole array each time
previous := k.getValidatorSigningBitArray(ctx, address, index)
if previous == signed {
// Array value at this index has not changed, no need to update counter
} else if previous && !signed {
// Array value has changed from signed to unsigned, decrement counter
k.setValidatorSigningBitArray(ctx, address, index, false)
signInfo.SignedBlocksCounter--
} else if !previous && signed {
// Array value has changed from unsigned to signed, increment counter
k.setValidatorSigningBitArray(ctx, address, index, true)
signInfo.SignedBlocksCounter++
}
minHeight := signInfo.StartHeight + SignedBlocksWindow
if height > minHeight && signInfo.SignedBlocksCounter < MinSignedPerWindow {
// Downtime confirmed, slash, revoke, and jail the validator
logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d", pubkey.Address(), minHeight, MinSignedPerWindow))
k.validatorSet.Slash(ctx, pubkey, height, SlashFractionDowntime)
k.validatorSet.Revoke(ctx, pubkey)
signInfo.JailedUntil = ctx.BlockHeader().Time + DowntimeUnbondDuration
}
// Set the updated signing info
k.setValidatorSigningInfo(ctx, address, signInfo)
}

131
x/slashing/keeper_test.go Normal file
View File

@ -0,0 +1,131 @@
package slashing
import (
"testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake"
)
func TestHandleDoubleSign(t *testing.T) {
// initial setup
ctx, ck, sk, keeper := createTestInput(t)
addr, val, amt := addrs[0], pks[0], int64(100)
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK())
stake.EndBlocker(ctx, sk)
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins - amt}})
require.Equal(t, sdk.NewRat(amt), sk.Validator(ctx, addr).GetPower())
// double sign less than max age
keeper.handleDoubleSign(ctx, 0, 0, val)
require.Equal(t, sdk.NewRat(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower())
ctx = ctx.WithBlockHeader(abci.Header{Time: 300})
// double sign past max age
keeper.handleDoubleSign(ctx, 0, 0, val)
require.Equal(t, sdk.NewRat(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower())
}
func TestHandleAbsentValidator(t *testing.T) {
// initial setup
ctx, ck, sk, keeper := createTestInput(t)
addr, val, amt := addrs[0], pks[0], int64(100)
sh := stake.NewHandler(sk)
slh := NewHandler(keeper)
got := sh(ctx, newTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK())
stake.EndBlocker(ctx, sk)
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins - amt}})
require.Equal(t, sdk.NewRat(amt), sk.Validator(ctx, addr).GetPower())
info, found := keeper.getValidatorSigningInfo(ctx, val.Address())
require.False(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(0), info.IndexOffset)
require.Equal(t, int64(0), info.SignedBlocksCounter)
require.Equal(t, int64(0), info.JailedUntil)
height := int64(0)
// 1000 first blocks OK
for ; height < 1000; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, true)
}
info, found = keeper.getValidatorSigningInfo(ctx, val.Address())
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, SignedBlocksWindow, info.SignedBlocksCounter)
// 50 blocks missed
for ; height < 1050; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, false)
}
info, found = keeper.getValidatorSigningInfo(ctx, val.Address())
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, SignedBlocksWindow-50, info.SignedBlocksCounter)
// validator should be bonded still
validator, _ := sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Bonded, validator.GetStatus())
pool := sk.GetPool(ctx)
require.Equal(t, int64(100), pool.BondedTokens)
// 51st block missed
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, false)
info, found = keeper.getValidatorSigningInfo(ctx, val.Address())
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, SignedBlocksWindow-51, info.SignedBlocksCounter)
// validator should have been revoked
validator, _ = sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Unbonded, validator.GetStatus())
// unrevocation should fail prior to jail expiration
got = slh(ctx, NewMsgUnrevoke(addr))
require.False(t, got.IsOK())
// unrevocation should succeed after jail expiration
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(86400 * 2)})
got = slh(ctx, NewMsgUnrevoke(addr))
require.True(t, got.IsOK())
// validator should be rebonded now
validator, _ = sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Bonded, validator.GetStatus())
// validator should have been slashed
pool = sk.GetPool(ctx)
require.Equal(t, int64(99), pool.BondedTokens)
// validator start height should have been changed
info, found = keeper.getValidatorSigningInfo(ctx, val.Address())
require.True(t, found)
require.Equal(t, height, info.StartHeight)
require.Equal(t, SignedBlocksWindow-51, info.SignedBlocksCounter)
// validator should not be immediately revoked again
height++
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, false)
validator, _ = sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Bonded, validator.GetStatus())
// validator should be revoked again after 100 unsigned blocks
nextHeight := height + 100
for ; height <= nextHeight; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val, false)
}
validator, _ = sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Unbonded, validator.GetStatus())
}

46
x/slashing/msg.go Normal file
View File

@ -0,0 +1,46 @@
package slashing
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
var cdc = wire.NewCodec()
// name to identify transaction types
const MsgType = "slashing"
// verify interface at compile time
var _ sdk.Msg = &MsgUnrevoke{}
// MsgUnrevoke - struct for unrevoking revoked validator
type MsgUnrevoke struct {
ValidatorAddr sdk.Address `json:"address"` // address of the validator owner
}
func NewMsgUnrevoke(validatorAddr sdk.Address) MsgUnrevoke {
return MsgUnrevoke{
ValidatorAddr: validatorAddr,
}
}
//nolint
func (msg MsgUnrevoke) Type() string { return MsgType }
func (msg MsgUnrevoke) GetSigners() []sdk.Address { return []sdk.Address{msg.ValidatorAddr} }
// get the bytes for the message signer to sign on
func (msg MsgUnrevoke) GetSignBytes() []byte {
b, err := cdc.MarshalJSON(msg)
if err != nil {
panic(err)
}
return b
}
// quick validity check
func (msg MsgUnrevoke) ValidateBasic() sdk.Error {
if msg.ValidatorAddr == nil {
return ErrBadValidatorAddr(DefaultCodespace)
}
return nil
}

37
x/slashing/params.go Normal file
View File

@ -0,0 +1,37 @@
package slashing
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
// MaxEvidenceAge - Max age for evidence - 21 days (3 weeks)
// TODO Should this be a governance parameter or just modifiable with SoftwareUpgradeProposals?
// MaxEvidenceAge = 60 * 60 * 24 * 7 * 3
// TODO Temporarily set to 2 minutes for testnets.
MaxEvidenceAge int64 = 60 * 2
// SignedBlocksWindow - sliding window for downtime slashing
// TODO Governance parameter?
// TODO Temporarily set to 100 blocks for testnets
SignedBlocksWindow int64 = 100
// Downtime slashing threshold - 50%
// TODO Governance parameter?
MinSignedPerWindow int64 = SignedBlocksWindow / 2
// Downtime unbond duration
// TODO Governance parameter?
// TODO Temporarily set to 10 minutes for testnets
DowntimeUnbondDuration int64 = 60 * 10
)
var (
// SlashFractionDoubleSign - currently 5%
// TODO Governance parameter?
SlashFractionDoubleSign = sdk.NewRat(1).Quo(sdk.NewRat(20))
// SlashFractionDowntime - currently 1%
// TODO Governance parameter?
SlashFractionDowntime = sdk.NewRat(1).Quo(sdk.NewRat(100))
)

View File

@ -0,0 +1,74 @@
package slashing
import (
"encoding/binary"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Stored by *validator* address (not owner address)
func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.Address) (info ValidatorSigningInfo, found bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(GetValidatorSigningInfoKey(address))
if bz == nil {
found = false
return
}
k.cdc.MustUnmarshalBinary(bz, &info)
found = true
return
}
// Stored by *validator* address (not owner address)
func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.Address, info ValidatorSigningInfo) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinary(info)
store.Set(GetValidatorSigningInfoKey(address), bz)
}
// Stored by *validator* address (not owner address)
func (k Keeper) getValidatorSigningBitArray(ctx sdk.Context, address sdk.Address, index int64) (signed bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(GetValidatorSigningBitArrayKey(address, index))
if bz == nil {
// lazy: treat empty key as unsigned
signed = false
return
}
k.cdc.MustUnmarshalBinary(bz, &signed)
return
}
// Stored by *validator* address (not owner address)
func (k Keeper) setValidatorSigningBitArray(ctx sdk.Context, address sdk.Address, index int64, signed bool) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinary(signed)
store.Set(GetValidatorSigningBitArrayKey(address, index), bz)
}
// Signing info for a validator
type ValidatorSigningInfo struct {
StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unrevoked
IndexOffset int64 `json:"index_offset"` // index offset into signed block bit array
JailedUntil int64 `json:"jailed_until"` // timestamp validator cannot be unrevoked until
SignedBlocksCounter int64 `json:"signed_blocks_counter"` // signed blocks counter (to avoid scanning the array every time)
}
// Return human readable signing info
func (i ValidatorSigningInfo) HumanReadableString() string {
return fmt.Sprintf("Start height: %d, index offset: %d, jailed until: %d, signed blocks counter: %d",
i.StartHeight, i.IndexOffset, i.JailedUntil, i.SignedBlocksCounter)
}
// Stored by *validator* address (not owner address)
func GetValidatorSigningInfoKey(v sdk.Address) []byte {
return append([]byte{0x01}, v.Bytes()...)
}
// Stored by *validator* address (not owner address)
func GetValidatorSigningBitArrayKey(v sdk.Address, i int64) []byte {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(i))
return append([]byte{0x02}, append(v.Bytes(), b...)...)
}

View File

@ -0,0 +1,35 @@
package slashing
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestGetSetValidatorSigningInfo(t *testing.T) {
ctx, _, _, keeper := createTestInput(t)
info, found := keeper.getValidatorSigningInfo(ctx, addrs[0])
require.False(t, found)
newInfo := ValidatorSigningInfo{
StartHeight: int64(4),
IndexOffset: int64(3),
JailedUntil: int64(2),
SignedBlocksCounter: int64(10),
}
keeper.setValidatorSigningInfo(ctx, addrs[0], newInfo)
info, found = keeper.getValidatorSigningInfo(ctx, addrs[0])
require.True(t, found)
require.Equal(t, info.StartHeight, int64(4))
require.Equal(t, info.IndexOffset, int64(3))
require.Equal(t, info.JailedUntil, int64(2))
require.Equal(t, info.SignedBlocksCounter, int64(10))
}
func TestGetSetValidatorSigningBitArray(t *testing.T) {
ctx, _, _, keeper := createTestInput(t)
signed := keeper.getValidatorSigningBitArray(ctx, addrs[0], 0)
require.False(t, signed) // treat empty key as unsigned
keeper.setValidatorSigningBitArray(ctx, addrs[0], 0, true)
signed = keeper.getValidatorSigningBitArray(ctx, addrs[0], 0)
require.True(t, signed) // now should be signed
}

97
x/slashing/test_common.go Normal file
View File

@ -0,0 +1,97 @@
package slashing
import (
"encoding/hex"
"os"
"testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/store"
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/stake"
)
var (
addrs = []sdk.Address{
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6160"),
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6161"),
testAddr("A58856F0FD53BF058B4909A21AEC019107BA6162"),
}
pks = []crypto.PubKey{
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB50"),
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51"),
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52"),
}
initCoins int64 = 200
)
func createTestCodec() *wire.Codec {
cdc := wire.NewCodec()
sdk.RegisterWire(cdc)
auth.RegisterWire(cdc)
bank.RegisterWire(cdc)
stake.RegisterWire(cdc)
wire.RegisterCrypto(cdc)
return cdc
}
func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, Keeper) {
keyAcc := sdk.NewKVStoreKey("acc")
keyStake := sdk.NewKVStoreKey("stake")
keySlashing := sdk.NewKVStoreKey("slashing")
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keySlashing, sdk.StoreTypeIAVL, db)
err := ms.LoadLatestVersion()
require.Nil(t, err)
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewTMLogger(os.Stdout))
cdc := createTestCodec()
accountMapper := auth.NewAccountMapper(cdc, keyAcc, &auth.BaseAccount{})
ck := bank.NewKeeper(accountMapper)
sk := stake.NewKeeper(cdc, keyStake, ck, stake.DefaultCodespace)
genesis := stake.DefaultGenesisState()
genesis.Pool.LooseUnbondedTokens = initCoins * int64(len(addrs))
stake.InitGenesis(ctx, sk, genesis)
for _, addr := range addrs {
ck.AddCoins(ctx, addr, sdk.Coins{
{sk.GetParams(ctx).BondDenom, initCoins},
})
}
keeper := NewKeeper(cdc, keySlashing, sk, DefaultCodespace)
return ctx, ck, sk, keeper
}
func newPubKey(pk string) (res crypto.PubKey) {
pkBytes, err := hex.DecodeString(pk)
if err != nil {
panic(err)
}
var pkEd crypto.PubKeyEd25519
copy(pkEd[:], pkBytes[:])
return pkEd
}
func testAddr(addr string) sdk.Address {
res := []byte(addr)
return res
}
func newTestMsgCreateValidator(address sdk.Address, pubKey crypto.PubKey, amt int64) stake.MsgCreateValidator {
return stake.MsgCreateValidator{
Description: stake.Description{},
ValidatorAddr: address,
PubKey: pubKey,
Bond: sdk.Coin{"steak", amt},
}
}

44
x/slashing/tick.go Normal file
View File

@ -0,0 +1,44 @@
package slashing
import (
"encoding/binary"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
)
// slashing begin block functionality
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) (tags sdk.Tags) {
// Tag the height
heightBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(heightBytes, uint64(req.Header.Height))
tags = sdk.NewTags("height", heightBytes)
// Deal with any equivocation evidence
for _, evidence := range req.ByzantineValidators {
pk, err := tmtypes.PB2TM.PubKey(evidence.Validator.PubKey)
if err != nil {
panic(err)
}
switch string(evidence.Type) {
case tmtypes.ABCIEvidenceTypeDuplicateVote:
sk.handleDoubleSign(ctx, evidence.Height, evidence.Time, pk)
default:
ctx.Logger().With("module", "x/slashing").Error(fmt.Sprintf("Ignored unknown evidence type: %s", string(evidence.Type)))
}
}
// Iterate over all the validators which *should* have signed this block
for _, validator := range req.Validators {
present := validator.SignedLastBlock
pubkey, err := tmtypes.PB2TM.PubKey(validator.Validator.PubKey)
if err != nil {
panic(err)
}
sk.handleValidatorSignature(ctx, pubkey, present)
}
return
}

77
x/slashing/tick_test.go Normal file
View File

@ -0,0 +1,77 @@
package slashing
import (
"testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake"
)
func TestBeginBlocker(t *testing.T) {
ctx, ck, sk, keeper := createTestInput(t)
addr, pk, amt := addrs[2], pks[2], int64(100)
// bond the validator
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, pk, amt))
require.True(t, got.IsOK())
stake.EndBlocker(ctx, sk)
require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins - amt}})
require.Equal(t, sdk.NewRat(amt), sk.Validator(ctx, addr).GetPower())
val := abci.Validator{
PubKey: tmtypes.TM2PB.PubKey(pk),
Power: amt,
}
// mark the validator as having signed
req := abci.RequestBeginBlock{
Validators: []abci.SigningValidator{{
Validator: val,
SignedLastBlock: true,
}},
}
BeginBlocker(ctx, req, keeper)
info, found := keeper.getValidatorSigningInfo(ctx, pk.Address())
require.True(t, found)
require.Equal(t, ctx.BlockHeight(), info.StartHeight)
require.Equal(t, int64(1), info.IndexOffset)
require.Equal(t, int64(0), info.JailedUntil)
require.Equal(t, int64(1), info.SignedBlocksCounter)
height := int64(0)
// for 50 blocks, mark the validator as having signed
for ; height < 50; height++ {
ctx = ctx.WithBlockHeight(height)
req = abci.RequestBeginBlock{
Validators: []abci.SigningValidator{{
Validator: val,
SignedLastBlock: true,
}},
}
BeginBlocker(ctx, req, keeper)
}
// for 51 blocks, mark the validator as having not signed
for ; height < 102; height++ {
ctx = ctx.WithBlockHeight(height)
req = abci.RequestBeginBlock{
Validators: []abci.SigningValidator{{
Validator: val,
SignedLastBlock: false,
}},
}
BeginBlocker(ctx, req, keeper)
}
// validator should be revoked
validator, found := sk.GetValidatorByPubKey(ctx, pk)
require.True(t, found)
require.Equal(t, sdk.Unbonded, validator.GetStatus())
}

12
x/slashing/wire.go Normal file
View File

@ -0,0 +1,12 @@
package slashing
import (
"github.com/cosmos/cosmos-sdk/wire"
)
// Register concrete types on wire codec
func RegisterWire(cdc *wire.Codec) {
cdc.RegisterConcrete(MsgUnrevoke{}, "cosmos-sdk/MsgUnrevoke", nil)
}
var cdcEmpty = wire.NewCodec()

View File

@ -21,7 +21,7 @@ func GetCmdQueryValidator(storeName string, cdc *wire.Codec) *cobra.Command {
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
addr, err := sdk.GetAccAddressBech32Cosmos(args[0]) addr, err := sdk.GetAccAddressBech32(args[0])
if err != nil { if err != nil {
return err return err
} }
@ -72,25 +72,25 @@ func GetCmdQueryValidators(storeName string, cdc *wire.Codec) *cobra.Command {
return err return err
} }
// parse out the candidates // parse out the validators
var candidates []stake.Validator var validators []stake.Validator
for _, KV := range resKVs { for _, KV := range resKVs {
var validator stake.Validator var validator stake.Validator
cdc.MustUnmarshalBinary(KV.Value, &validator) cdc.MustUnmarshalBinary(KV.Value, &validator)
candidates = append(candidates, validator) validators = append(validators, validator)
} }
switch viper.Get(cli.OutputFlag) { switch viper.Get(cli.OutputFlag) {
case "text": case "text":
for _, candidate := range candidates { for _, validator := range validators {
resp, err := candidate.HumanReadableString() resp, err := validator.HumanReadableString()
if err != nil { if err != nil {
return err return err
} }
fmt.Println(resp) fmt.Println(resp)
} }
case "json": case "json":
output, err := wire.MarshalJSONIndent(cdc, candidates) output, err := wire.MarshalJSONIndent(cdc, validators)
if err != nil { if err != nil {
return err return err
} }
@ -112,7 +112,7 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
Short: "Query a delegations bond based on address and validator address", Short: "Query a delegations bond based on address and validator address",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
addr, err := sdk.GetAccAddressBech32Cosmos(viper.GetString(FlagAddressValidator)) addr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
if err != nil { if err != nil {
return err return err
} }
@ -157,7 +157,7 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
return cmd return cmd
} }
// get the command to query all the candidates bonded to a delegation // get the command to query all the validators bonded to a delegation
func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command { func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "delegations [delegator-addr]", Use: "delegations [delegator-addr]",
@ -165,7 +165,7 @@ func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
delegatorAddr, err := sdk.GetAccAddressBech32Cosmos(args[0]) delegatorAddr, err := sdk.GetAccAddressBech32(args[0])
if err != nil { if err != nil {
return err return err
} }
@ -176,7 +176,7 @@ func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
return err return err
} }
// parse out the candidates // parse out the validators
var delegations []stake.Delegation var delegations []stake.Delegation
for _, KV := range resKVs { for _, KV := range resKVs {
var delegation stake.Delegation var delegation stake.Delegation

View File

@ -13,8 +13,8 @@ import (
"github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake"
) )
// create declare candidacy command // create create validator command
func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command { func GetCmdCreateValidator(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "create-validator", Use: "create-validator",
Short: "create new validator initialized with a self-delegation to it", Short: "create new validator initialized with a self-delegation to it",
@ -25,7 +25,7 @@ func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command {
if err != nil { if err != nil {
return err return err
} }
validatorAddr, err := sdk.GetAccAddressBech32Cosmos(viper.GetString(FlagAddressValidator)) validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
if err != nil { if err != nil {
return err return err
} }
@ -34,7 +34,7 @@ func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command {
if len(pkStr) == 0 { if len(pkStr) == 0 {
return fmt.Errorf("must use --pubkey flag") return fmt.Errorf("must use --pubkey flag")
} }
pk, err := sdk.GetValPubKeyBech32Cosmos(pkStr) pk, err := sdk.GetValPubKeyBech32(pkStr)
if err != nil { if err != nil {
return err return err
} }
@ -47,7 +47,7 @@ func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command {
Website: viper.GetString(FlagWebsite), Website: viper.GetString(FlagWebsite),
Details: viper.GetString(FlagDetails), Details: viper.GetString(FlagDetails),
} }
msg := stake.NewMsgDeclareCandidacy(validatorAddr, pk, amount, description) msg := stake.NewMsgCreateValidator(validatorAddr, pk, amount, description)
// build and sign the transaction, then broadcast to Tendermint // build and sign the transaction, then broadcast to Tendermint
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, msg, cdc) res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, msg, cdc)
@ -67,14 +67,14 @@ func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command {
return cmd return cmd
} }
// create edit candidacy command // create edit validator command
func GetCmdEditCandidacy(cdc *wire.Codec) *cobra.Command { func GetCmdEditValidator(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "edit-validator", Use: "edit-validator",
Short: "edit and existing validator account", Short: "edit and existing validator account",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
validatorAddr, err := sdk.GetAccAddressBech32Cosmos(viper.GetString(FlagAddressValidator)) validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
if err != nil { if err != nil {
return err return err
} }
@ -84,7 +84,7 @@ func GetCmdEditCandidacy(cdc *wire.Codec) *cobra.Command {
Website: viper.GetString(FlagWebsite), Website: viper.GetString(FlagWebsite),
Details: viper.GetString(FlagDetails), Details: viper.GetString(FlagDetails),
} }
msg := stake.NewMsgEditCandidacy(validatorAddr, description) msg := stake.NewMsgEditValidator(validatorAddr, description)
// build and sign the transaction, then broadcast to Tendermint // build and sign the transaction, then broadcast to Tendermint
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc)) ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
@ -104,7 +104,7 @@ func GetCmdEditCandidacy(cdc *wire.Codec) *cobra.Command {
return cmd return cmd
} }
// create edit candidacy command // create edit validator command
func GetCmdDelegate(cdc *wire.Codec) *cobra.Command { func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "delegate", Use: "delegate",
@ -115,8 +115,8 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
return err return err
} }
delegatorAddr, err := sdk.GetAccAddressBech32Cosmos(viper.GetString(FlagAddressDelegator)) delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
validatorAddr, err := sdk.GetAccAddressBech32Cosmos(viper.GetString(FlagAddressValidator)) validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
if err != nil { if err != nil {
return err return err
} }
@ -142,7 +142,7 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
return cmd return cmd
} }
// create edit candidacy command // create edit validator command
func GetCmdUnbond(cdc *wire.Codec) *cobra.Command { func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "unbond", Use: "unbond",
@ -163,8 +163,8 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
} }
} }
delegatorAddr, err := sdk.GetAccAddressBech32Cosmos(viper.GetString(FlagAddressDelegator)) delegatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressDelegator))
validatorAddr, err := sdk.GetAccAddressBech32Cosmos(viper.GetString(FlagAddressValidator)) validatorAddr, err := sdk.GetAccAddressBech32(viper.GetString(FlagAddressValidator))
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,12 +1,10 @@
package rest package rest
import ( import (
"encoding/hex"
"fmt" "fmt"
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/tendermint/go-crypto/keys"
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
@ -14,34 +12,39 @@ import (
"github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake"
) )
// RegisterRoutes - Central function to define routes that get registered by the main application func registerQueryRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) {
func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) { r.HandleFunc(
r.HandleFunc("/stake/{delegator}/bonding_status/{validator}", BondingStatusHandlerFn("stake", cdc, kb, ctx)).Methods("GET") "/stake/{delegator}/bonding_status/{validator}",
bondingStatusHandlerFn(ctx, "stake", cdc),
).Methods("GET")
r.HandleFunc(
"/stake/validators",
validatorsHandlerFn(ctx, "stake", cdc),
).Methods("GET")
} }
// BondingStatusHandlerFn - http request handler to query delegator bonding status // http request handler to query delegator bonding status
func BondingStatusHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc { func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
// read parameters // read parameters
vars := mux.Vars(r) vars := mux.Vars(r)
delegator := vars["delegator"] bech32delegator := vars["delegator"]
validator := vars["validator"] bech32validator := vars["validator"]
bz, err := hex.DecodeString(delegator) delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
return return
} }
delegatorAddr := sdk.Address(bz)
bz, err = hex.DecodeString(validator) validatorAddr, err := sdk.GetValAddressBech32(bech32validator)
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
return return
} }
validatorAddr := sdk.Address(bz)
key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc) key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc)
@ -76,3 +79,103 @@ func BondingStatusHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase,
w.Write(output) w.Write(output)
} }
} }
// TODO inherit from Validator
type StakeValidatorOutput struct {
Owner string `json:"owner"` // in bech32
PubKey string `json:"pub_key"` // in bech32
Revoked bool `json:"revoked"` // has the validator been revoked from bonded status?
PoolShares stake.PoolShares `json:"pool_shares"` // total shares for tokens held in the pool
DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators
Description stake.Description `json:"description"` // description terms for the validator
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators
CommissionMax sdk.Rat `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
CommissionChangeRate sdk.Rat `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
// fee related
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools
}
func bech32StakeValidatorOutput(validator stake.Validator) (StakeValidatorOutput, error) {
bechOwner, err := sdk.Bech32ifyVal(validator.Owner)
if err != nil {
return StakeValidatorOutput{}, err
}
bechValPubkey, err := sdk.Bech32ifyValPub(validator.PubKey)
if err != nil {
return StakeValidatorOutput{}, err
}
return StakeValidatorOutput{
Owner: bechOwner,
PubKey: bechValPubkey,
Revoked: validator.Revoked,
PoolShares: validator.PoolShares,
DelegatorShares: validator.DelegatorShares,
Description: validator.Description,
BondHeight: validator.BondHeight,
BondIntraTxCounter: validator.BondIntraTxCounter,
ProposerRewardPool: validator.ProposerRewardPool,
Commission: validator.Commission,
CommissionMax: validator.CommissionMax,
CommissionChangeRate: validator.CommissionChangeRate,
CommissionChangeToday: validator.CommissionChangeToday,
PrevBondedShares: validator.PrevBondedShares,
}, nil
}
// TODO bech32
// http request handler to query list of validators
func validatorsHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
kvs, err := ctx.QuerySubspace(cdc, stake.ValidatorsKey, storeName)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't query validators. Error: %s", err.Error())))
return
}
// the query will return empty if there are no validators
if len(kvs) == 0 {
w.WriteHeader(http.StatusNoContent)
return
}
// parse out the validators
validators := make([]StakeValidatorOutput, len(kvs))
for i, kv := range kvs {
var validator stake.Validator
var bech32Validator StakeValidatorOutput
err = cdc.UnmarshalBinary(kv.Value, &validator)
if err == nil {
bech32Validator, err = bech32StakeValidatorOutput(validator)
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
return
}
validators[i] = bech32Validator
}
output, err := cdc.MarshalJSON(validators)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}

View File

@ -0,0 +1,15 @@
package rest
import (
"github.com/gorilla/mux"
"github.com/tendermint/go-crypto/keys"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/wire"
)
// RegisterRoutes registers staking-related REST handlers to a router
func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) {
registerQueryRoutes(ctx, r, cdc)
registerTxRoutes(ctx, r, cdc, kb)
}

164
x/stake/client/rest/tx.go Normal file
View File

@ -0,0 +1,164 @@
package rest
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
"github.com/tendermint/go-crypto/keys"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/stake"
)
func registerTxRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) {
r.HandleFunc(
"/stake/delegations",
editDelegationsRequestHandlerFn(cdc, kb, ctx),
).Methods("POST")
}
type msgDelegateInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorAddr string `json:"validator_addr"` // in bech32
Bond sdk.Coin `json:"bond"`
}
type msgUnbondInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorAddr string `json:"validator_addr"` // in bech32
Shares string `json:"shares"`
}
type editDelegationsBody struct {
LocalAccountName string `json:"name"`
Password string `json:"password"`
ChainID string `json:"chain_id"`
Sequence int64 `json:"sequence"`
Delegate []msgDelegateInput `json:"delegate"`
Unbond []msgUnbondInput `json:"unbond"`
}
func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m editDelegationsBody
body, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
err = json.Unmarshal(body, &m)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
info, err := kb.Get(m.LocalAccountName)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(err.Error()))
return
}
// build messages
messages := make([]sdk.Msg, len(m.Delegate)+len(m.Unbond))
i := 0
for _, msg := range m.Delegate {
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
return
}
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
return
}
if !bytes.Equal(info.Address(), delegatorAddr) {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Must use own delegator address"))
return
}
messages[i] = stake.MsgDelegate{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validatorAddr,
Bond: msg.Bond,
}
i++
}
for _, msg := range m.Unbond {
delegatorAddr, err := sdk.GetAccAddressBech32(msg.DelegatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())))
return
}
validatorAddr, err := sdk.GetValAddressBech32(msg.ValidatorAddr)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())))
return
}
if !bytes.Equal(info.Address(), delegatorAddr) {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Must use own delegator address"))
return
}
messages[i] = stake.MsgUnbond{
DelegatorAddr: delegatorAddr,
ValidatorAddr: validatorAddr,
Shares: msg.Shares,
}
i++
}
// sign messages
signedTxs := make([][]byte, len(messages[:]))
for i, msg := range messages {
// increment sequence for each message
ctx = ctx.WithSequence(m.Sequence)
m.Sequence++
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, cdc)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(err.Error()))
return
}
signedTxs[i] = txBytes
}
// send
// XXX the operation might not be atomic if a tx fails
// should we have a sdk.MultiMsg type to make sending atomic?
results := make([]*ctypes.ResultBroadcastTxCommit, len(signedTxs[:]))
for i, txBytes := range signedTxs {
res, err := ctx.BroadcastTx(txBytes)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
results[i] = res
}
output, err := json.MarshalIndent(results[:], "", " ")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}

View File

@ -35,11 +35,11 @@ func (b Delegation) GetBondShares() sdk.Rat { return b.Shares }
//Human Friendly pretty printer //Human Friendly pretty printer
func (b Delegation) HumanReadableString() (string, error) { func (b Delegation) HumanReadableString() (string, error) {
bechAcc, err := sdk.Bech32CosmosifyAcc(b.DelegatorAddr) bechAcc, err := sdk.Bech32ifyAcc(b.DelegatorAddr)
if err != nil { if err != nil {
return "", err return "", err
} }
bechVal, err := sdk.Bech32CosmosifyAcc(b.ValidatorAddr) bechVal, err := sdk.Bech32ifyAcc(b.ValidatorAddr)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -16,6 +16,7 @@ const (
CodeInvalidValidator CodeType = 201 CodeInvalidValidator CodeType = 201
CodeInvalidBond CodeType = 202 CodeInvalidBond CodeType = 202
CodeInvalidInput CodeType = 203 CodeInvalidInput CodeType = 203
CodeValidatorJailed CodeType = 204
CodeUnauthorized CodeType = sdk.CodeUnauthorized CodeUnauthorized CodeType = sdk.CodeUnauthorized
CodeInternal CodeType = sdk.CodeInternal CodeInternal CodeType = sdk.CodeInternal
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
@ -72,10 +73,10 @@ func ErrBadDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Delegator does not exist for that address") return newError(codespace, CodeInvalidValidator, "Delegator does not exist for that address")
} }
func ErrValidatorExistsAddr(codespace sdk.CodespaceType) sdk.Error { func ErrValidatorExistsAddr(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Validator already exist, cannot re-declare candidacy") return newError(codespace, CodeInvalidValidator, "Validator already exist, cannot re-create validator")
} }
func ErrValidatorRevoked(codespace sdk.CodespaceType) sdk.Error { func ErrValidatorRevoked(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Candidacy for this address is currently revoked") return newError(codespace, CodeInvalidValidator, "Validator for this address is currently revoked")
} }
func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error { func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidValidator, "Missing signature") return newError(codespace, CodeInvalidValidator, "Missing signature")

View File

@ -1,6 +1,10 @@
package stake package stake
import sdk "github.com/cosmos/cosmos-sdk/types" import (
tmtypes "github.com/tendermint/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// GenesisState - all staking state that must be provided at genesis // GenesisState - all staking state that must be provided at genesis
type GenesisState struct { type GenesisState struct {
@ -22,21 +26,32 @@ func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []D
// get raw genesis raw message for testing // get raw genesis raw message for testing
func DefaultGenesisState() GenesisState { func DefaultGenesisState() GenesisState {
return GenesisState{ return GenesisState{
Pool: initialPool(), Pool: InitialPool(),
Params: defaultParams(), Params: DefaultParams(),
} }
} }
// InitGenesis - store genesis parameters // InitGenesis - store genesis parameters
func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
store := ctx.KVStore(k.storeKey)
k.setPool(ctx, data.Pool) k.setPool(ctx, data.Pool)
k.setNewParams(ctx, data.Params) k.setNewParams(ctx, data.Params)
for _, validator := range data.Validators { for _, validator := range data.Validators {
k.updateValidator(ctx, validator)
// set validator
k.setValidator(ctx, validator)
// manually set indexes for the first time
k.setValidatorByPubKeyIndex(ctx, validator)
k.setValidatorByPowerIndex(ctx, validator, data.Pool)
if validator.Status() == sdk.Bonded {
store.Set(GetValidatorsBondedKey(validator.PubKey), validator.Owner)
}
} }
for _, bond := range data.Bonds { for _, bond := range data.Bonds {
k.setDelegation(ctx, bond) k.setDelegation(ctx, bond)
} }
k.updateBondedValidatorsFull(ctx, store)
} }
// WriteGenesis - output genesis parameters // WriteGenesis - output genesis parameters
@ -52,3 +67,16 @@ func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
bonds, bonds,
} }
} }
// WriteValidators - output current validator set
func WriteValidators(ctx sdk.Context, k Keeper) (vals []tmtypes.GenesisValidator) {
k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
vals = append(vals, tmtypes.GenesisValidator{
PubKey: validator.GetPubKey(),
Power: validator.GetPower().Evaluate(),
Name: validator.GetMoniker(),
})
return false
})
return
}

View File

@ -11,10 +11,10 @@ func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
// NOTE msg already has validate basic run // NOTE msg already has validate basic run
switch msg := msg.(type) { switch msg := msg.(type) {
case MsgDeclareCandidacy: case MsgCreateValidator:
return handleMsgDeclareCandidacy(ctx, msg, k) return handleMsgCreateValidator(ctx, msg, k)
case MsgEditCandidacy: case MsgEditValidator:
return handleMsgEditCandidacy(ctx, msg, k) return handleMsgEditValidator(ctx, msg, k)
case MsgDelegate: case MsgDelegate:
return handleMsgDelegate(ctx, msg, k) return handleMsgDelegate(ctx, msg, k)
case MsgUnbond: case MsgUnbond:
@ -25,13 +25,27 @@ func NewHandler(k Keeper) sdk.Handler {
} }
} }
// NewEndBlocker generates sdk.EndBlocker // Called every block, process inflation, update validator set
// Performs tick functionality func EndBlocker(ctx sdk.Context, k Keeper) (ValidatorUpdates []abci.Validator) {
func NewEndBlocker(k Keeper) sdk.EndBlocker { pool := k.GetPool(ctx)
return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
res.ValidatorUpdates = k.Tick(ctx) // Process Validator Provisions
return blockTime := ctx.BlockHeader().Time // XXX assuming in seconds, confirm
if pool.InflationLastTime+blockTime >= 3600 {
pool.InflationLastTime = blockTime
pool = k.processProvisions(ctx)
} }
// save the params
k.setPool(ctx, pool)
// reset the intra-transaction counter
k.setIntraTxCounter(ctx, 0)
// calculate validator set changes
ValidatorUpdates = k.getTendermintUpdates(ctx)
k.clearTendermintUpdates(ctx)
return
} }
//_____________________________________________________________________ //_____________________________________________________________________
@ -39,7 +53,7 @@ func NewEndBlocker(k Keeper) sdk.EndBlocker {
// These functions assume everything has been authenticated, // These functions assume everything has been authenticated,
// now we just perform action and save // now we just perform action and save
func handleMsgDeclareCandidacy(ctx sdk.Context, msg MsgDeclareCandidacy, k Keeper) sdk.Result { func handleMsgCreateValidator(ctx sdk.Context, msg MsgCreateValidator, k Keeper) sdk.Result {
// check to see if the pubkey or sender has been registered before // check to see if the pubkey or sender has been registered before
_, found := k.GetValidator(ctx, msg.ValidatorAddr) _, found := k.GetValidator(ctx, msg.ValidatorAddr)
@ -55,8 +69,9 @@ func handleMsgDeclareCandidacy(ctx sdk.Context, msg MsgDeclareCandidacy, k Keepe
validator := NewValidator(msg.ValidatorAddr, msg.PubKey, msg.Description) validator := NewValidator(msg.ValidatorAddr, msg.PubKey, msg.Description)
k.setValidator(ctx, validator) k.setValidator(ctx, validator)
k.setValidatorByPubKeyIndex(ctx, validator)
tags := sdk.NewTags( tags := sdk.NewTags(
"action", []byte("declareCandidacy"), "action", []byte("createValidator"),
"validator", msg.ValidatorAddr.Bytes(), "validator", msg.ValidatorAddr.Bytes(),
"moniker", []byte(msg.Description.Moniker), "moniker", []byte(msg.Description.Moniker),
"identity", []byte(msg.Description.Identity), "identity", []byte(msg.Description.Identity),
@ -74,7 +89,7 @@ func handleMsgDeclareCandidacy(ctx sdk.Context, msg MsgDeclareCandidacy, k Keepe
} }
} }
func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk.Result { func handleMsgEditValidator(ctx sdk.Context, msg MsgEditValidator, k Keeper) sdk.Result {
// validator must already be registered // validator must already be registered
validator, found := k.GetValidator(ctx, msg.ValidatorAddr) validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
@ -94,7 +109,7 @@ func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk
k.updateValidator(ctx, validator) k.updateValidator(ctx, validator)
tags := sdk.NewTags( tags := sdk.NewTags(
"action", []byte("editCandidacy"), "action", []byte("editValidator"),
"validator", msg.ValidatorAddr.Bytes(), "validator", msg.ValidatorAddr.Bytes(),
"moniker", []byte(msg.Description.Moniker), "moniker", []byte(msg.Description.Moniker),
"identity", []byte(msg.Description.Identity), "identity", []byte(msg.Description.Identity),
@ -206,14 +221,14 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
bond.Shares = bond.Shares.Sub(delShares) bond.Shares = bond.Shares.Sub(delShares)
// remove the bond // remove the bond
revokeCandidacy := false revokeValidator := false
if bond.Shares.IsZero() { if bond.Shares.IsZero() {
// if the bond is the owner of the validator then // if the bond is the owner of the validator then
// trigger a revoke candidacy // trigger a revoke validator
if bytes.Equal(bond.DelegatorAddr, validator.Owner) && if bytes.Equal(bond.DelegatorAddr, validator.Owner) &&
validator.Revoked == false { validator.Revoked == false {
revokeCandidacy = true revokeValidator = true
} }
k.removeDelegation(ctx, bond) k.removeDelegation(ctx, bond)
@ -232,7 +247,7 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
///////////////////////////////////// /////////////////////////////////////
// revoke validator if necessary // revoke validator if necessary
if revokeCandidacy { if revokeValidator {
validator.Revoked = true validator.Revoked = true
} }

View File

@ -14,8 +14,8 @@ import (
//______________________________________________________________________ //______________________________________________________________________
func newTestMsgDeclareCandidacy(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgDeclareCandidacy { func newTestMsgCreateValidator(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgCreateValidator {
return MsgDeclareCandidacy{ return MsgCreateValidator{
Description: Description{}, Description: Description{},
ValidatorAddr: address, ValidatorAddr: address,
PubKey: pubKey, PubKey: pubKey,
@ -33,13 +33,13 @@ func newTestMsgDelegate(delegatorAddr, validatorAddr sdk.Address, amt int64) Msg
//______________________________________________________________________ //______________________________________________________________________
func TestDuplicatesMsgDeclareCandidacy(t *testing.T) { func TestDuplicatesMsgCreateValidator(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, 1000) ctx, _, keeper := createTestInput(t, false, 1000)
validatorAddr := addrs[0] validatorAddr := addrs[0]
pk := pks[0] pk := pks[0]
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pk, 10) msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pk, 10)
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.True(t, got.IsOK(), "%v", got) assert.True(t, got.IsOK(), "%v", got)
validator, found := keeper.GetValidator(ctx, validatorAddr) validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found) require.True(t, found)
@ -51,8 +51,8 @@ func TestDuplicatesMsgDeclareCandidacy(t *testing.T) {
assert.Equal(t, Description{}, validator.Description) assert.Equal(t, Description{}, validator.Description)
// one validator cannot bond twice // one validator cannot bond twice
msgDeclareCandidacy.PubKey = pks[1] msgCreateValidator.PubKey = pks[1]
got = handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.False(t, got.IsOK(), "%v", got) assert.False(t, got.IsOK(), "%v", got)
} }
@ -64,10 +64,10 @@ func TestIncrementsMsgDelegate(t *testing.T) {
bondAmount := int64(10) bondAmount := int64(10)
validatorAddr, delegatorAddr := addrs[0], addrs[1] validatorAddr, delegatorAddr := addrs[0], addrs[1]
// first declare candidacy // first create validator
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pks[0], bondAmount) msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], bondAmount)
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.True(t, got.IsOK(), "expected declare candidacy msg to be ok, got %v", got) assert.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
validator, found := keeper.GetValidator(ctx, validatorAddr) validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found) require.True(t, found)
@ -134,12 +134,12 @@ func TestIncrementsMsgUnbond(t *testing.T) {
ctx, accMapper, keeper := createTestInput(t, false, initBond) ctx, accMapper, keeper := createTestInput(t, false, initBond)
params := keeper.GetParams(ctx) params := keeper.GetParams(ctx)
// declare candidacy, delegate // create validator, delegate
validatorAddr, delegatorAddr := addrs[0], addrs[1] validatorAddr, delegatorAddr := addrs[0], addrs[1]
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pks[0], initBond) msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], initBond)
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.True(t, got.IsOK(), "expected declare-candidacy to be ok, got %v", got) assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, initBond) msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, initBond)
got = handleMsgDelegate(ctx, msgDelegate, keeper) got = handleMsgDelegate(ctx, msgDelegate, keeper)
@ -216,7 +216,7 @@ func TestIncrementsMsgUnbond(t *testing.T) {
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded) "got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
} }
func TestMultipleMsgDeclareCandidacy(t *testing.T) { func TestMultipleMsgCreateValidator(t *testing.T) {
initBond := int64(1000) initBond := int64(1000)
ctx, accMapper, keeper := createTestInput(t, false, initBond) ctx, accMapper, keeper := createTestInput(t, false, initBond)
params := keeper.GetParams(ctx) params := keeper.GetParams(ctx)
@ -224,8 +224,8 @@ func TestMultipleMsgDeclareCandidacy(t *testing.T) {
// bond them all // bond them all
for i, validatorAddr := range validatorAddrs { for i, validatorAddr := range validatorAddrs {
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pks[i], 10) msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[i], 10)
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
//Check that the account is bonded //Check that the account is bonded
@ -266,8 +266,8 @@ func TestMultipleMsgDelegate(t *testing.T) {
validatorAddr, delegatorAddrs := addrs[0], addrs[1:] validatorAddr, delegatorAddrs := addrs[0], addrs[1:]
//first make a validator //first make a validator
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pks[0], 10) msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], 10)
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got) require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
// delegate multiple parties // delegate multiple parties
@ -294,14 +294,14 @@ func TestMultipleMsgDelegate(t *testing.T) {
} }
} }
func TestVoidCandidacy(t *testing.T) { func TestRevokeValidator(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, 1000) ctx, _, keeper := createTestInput(t, false, 1000)
validatorAddr, delegatorAddr := addrs[0], addrs[1] validatorAddr, delegatorAddr := addrs[0], addrs[1]
// create the validator // create the validator
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pks[0], 10) msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], 10)
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgDeclareCandidacy") require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// bond a delegator // bond a delegator
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10) msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
@ -311,7 +311,7 @@ func TestVoidCandidacy(t *testing.T) {
// unbond the validators bond portion // unbond the validators bond portion
msgUnbondValidator := NewMsgUnbond(validatorAddr, validatorAddr, "10") msgUnbondValidator := NewMsgUnbond(validatorAddr, validatorAddr, "10")
got = handleMsgUnbond(ctx, msgUnbondValidator, keeper) got = handleMsgUnbond(ctx, msgUnbondValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgDeclareCandidacy") require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
validator, found := keeper.GetValidator(ctx, validatorAddr) validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found) require.True(t, found)
require.True(t, validator.Revoked) require.True(t, validator.Revoked)
@ -323,9 +323,9 @@ func TestVoidCandidacy(t *testing.T) {
// test that the delegator can still withdraw their bonds // test that the delegator can still withdraw their bonds
msgUnbondDelegator := NewMsgUnbond(delegatorAddr, validatorAddr, "10") msgUnbondDelegator := NewMsgUnbond(delegatorAddr, validatorAddr, "10")
got = handleMsgUnbond(ctx, msgUnbondDelegator, keeper) got = handleMsgUnbond(ctx, msgUnbondDelegator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgDeclareCandidacy") require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// verify that the pubkey can now be reused // verify that the pubkey can now be reused
got = handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
assert.True(t, got.IsOK(), "expected ok, got %v", got) assert.True(t, got.IsOK(), "expected ok, got %v", got)
} }

View File

@ -2,7 +2,6 @@ package stake
import ( import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/abci/types"
) )
const ( const (
@ -12,30 +11,6 @@ const (
var hrsPerYrRat = sdk.NewRat(hrsPerYr) // 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) {
p := k.GetPool(ctx)
// Process Validator Provisions
blockTime := ctx.BlockHeader().Time // XXX assuming in seconds, confirm
if p.InflationLastTime+blockTime >= 3600 {
p.InflationLastTime = blockTime
p = k.processProvisions(ctx)
}
// save the params
k.setPool(ctx, p)
// reset the intra-transaction counter
k.setIntraTxCounter(ctx, 0)
// calculate validator set changes
change = k.getTendermintUpdates(ctx)
k.clearTendermintUpdates(ctx)
return change
}
// process provisions for an hour period // process provisions for an hour period
func (k Keeper) processProvisions(ctx sdk.Context) Pool { func (k Keeper) processProvisions(ctx sdk.Context) Pool {

View File

@ -8,6 +8,7 @@ import (
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/bank"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
) )
// keeper of the staking store // keeper of the staking store
@ -38,6 +39,16 @@ func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.Address) (validator Valid
return k.getValidator(store, addr) return k.getValidator(store, addr)
} }
// get a single validator by pubkey
func (k Keeper) GetValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) (validator Validator, found bool) {
store := ctx.KVStore(k.storeKey)
addr := store.Get(GetValidatorByPubKeyIndexKey(pubkey))
if addr == nil {
return validator, false
}
return k.getValidator(store, addr)
}
// get a single validator (reuse store) // get a single validator (reuse store)
func (k Keeper) getValidator(store sdk.KVStore, addr sdk.Address) (validator Validator, found bool) { func (k Keeper) getValidator(store sdk.KVStore, addr sdk.Address) (validator Validator, found bool) {
b := store.Get(GetValidatorKey(addr)) b := store.Get(GetValidatorKey(addr))
@ -51,10 +62,22 @@ func (k Keeper) getValidator(store sdk.KVStore, addr sdk.Address) (validator Val
// set the main record holding validator details // set the main record holding validator details
func (k Keeper) setValidator(ctx sdk.Context, validator Validator) { func (k Keeper) setValidator(ctx sdk.Context, validator Validator) {
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
// set main store
bz := k.cdc.MustMarshalBinary(validator) bz := k.cdc.MustMarshalBinary(validator)
store.Set(GetValidatorKey(validator.Owner), bz) store.Set(GetValidatorKey(validator.Owner), bz)
} }
func (k Keeper) setValidatorByPubKeyIndex(ctx sdk.Context, validator Validator) {
store := ctx.KVStore(k.storeKey)
// set pointer by pubkey
store.Set(GetValidatorByPubKeyIndexKey(validator.PubKey), validator.Owner)
}
func (k Keeper) setValidatorByPowerIndex(ctx sdk.Context, validator Validator, pool Pool) {
store := ctx.KVStore(k.storeKey)
store.Set(GetValidatorsByPowerKey(validator, pool), validator.Owner)
}
// Get the set of all validators with no limits, used during genesis dump // Get the set of all validators with no limits, used during genesis dump
func (k Keeper) getAllValidators(ctx sdk.Context) (validators Validators) { func (k Keeper) getAllValidators(ctx sdk.Context) (validators Validators) {
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
@ -203,8 +226,12 @@ func (k Keeper) updateValidator(ctx sdk.Context, validator Validator) Validator
oldValidator, oldFound := k.GetValidator(ctx, ownerAddr) oldValidator, oldFound := k.GetValidator(ctx, ownerAddr)
if validator.Revoked && oldValidator.Status() == sdk.Bonded { if validator.Revoked && oldValidator.Status() == sdk.Bonded {
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded) validator = k.unbondValidator(ctx, store, validator)
k.setPool(ctx, pool)
// need to also clear the cliff validator spot because the revoke has
// opened up a new spot which will be filled when
// updateValidatorsBonded is called
k.clearCliffValidator(ctx)
} }
powerIncreasing := false powerIncreasing := false
@ -409,7 +436,7 @@ func (k Keeper) updateBondedValidatorsFull(ctx sdk.Context, store sdk.KVStore) {
} }
// perform all the store operations for when a validator status becomes unbonded // perform all the store operations for when a validator status becomes unbonded
func (k Keeper) unbondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) { func (k Keeper) unbondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) Validator {
pool := k.GetPool(ctx) pool := k.GetPool(ctx)
// sanity check // sanity check
@ -431,6 +458,7 @@ func (k Keeper) unbondValidator(ctx sdk.Context, store sdk.KVStore, validator Va
// also remove from the Bonded Validators Store // also remove from the Bonded Validators Store
store.Delete(GetValidatorsBondedKey(validator.PubKey)) store.Delete(GetValidatorsBondedKey(validator.PubKey))
return validator
} }
// perform all the store operations for when a validator status becomes bonded // perform all the store operations for when a validator status becomes bonded
@ -470,6 +498,7 @@ func (k Keeper) removeValidator(ctx sdk.Context, address sdk.Address) {
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
pool := k.getPool(store) pool := k.getPool(store)
store.Delete(GetValidatorKey(address)) store.Delete(GetValidatorKey(address))
store.Delete(GetValidatorByPubKeyIndexKey(validator.PubKey))
store.Delete(GetValidatorsByPowerKey(validator, pool)) store.Delete(GetValidatorsByPowerKey(validator, pool))
// delete from the current and power weighted validator groups if the validator // delete from the current and power weighted validator groups if the validator
@ -607,9 +636,9 @@ func (k Keeper) getPool(store sdk.KVStore) (pool Pool) {
return return
} }
func (k Keeper) setPool(ctx sdk.Context, p Pool) { func (k Keeper) setPool(ctx sdk.Context, pool Pool) {
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(p) b := k.cdc.MustMarshalBinary(pool)
store.Set(PoolKey, b) store.Set(PoolKey, b)
} }
@ -656,6 +685,13 @@ func (k Keeper) setCliffValidator(ctx sdk.Context, validator Validator, pool Poo
store.Set(ValidatorCliffKey, validator.Owner) store.Set(ValidatorCliffKey, validator.Owner)
} }
// clear the current validator and power of the validator on the cliff
func (k Keeper) clearCliffValidator(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
store.Delete(ValidatorPowerCliffKey)
store.Delete(ValidatorCliffKey)
}
//__________________________________________________________________________ //__________________________________________________________________________
// Implements ValidatorSet // Implements ValidatorSet
@ -749,3 +785,46 @@ func (k Keeper) IterateDelegators(ctx sdk.Context, delAddr sdk.Address, fn func(
} }
iterator.Close() iterator.Close()
} }
// slash a validator
func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, fraction sdk.Rat) {
// TODO height ignored for now, see https://github.com/cosmos/cosmos-sdk/pull/1011#issuecomment-390253957
logger := ctx.Logger().With("module", "x/stake")
val, found := k.GetValidatorByPubKey(ctx, pubkey)
if !found {
panic(fmt.Errorf("Attempted to slash a nonexistent validator with address %s", pubkey.Address()))
}
sharesToRemove := val.PoolShares.Amount.Mul(fraction)
pool := k.GetPool(ctx)
val, pool, burned := val.removePoolShares(pool, sharesToRemove)
k.setPool(ctx, pool) // update the pool
k.updateValidator(ctx, val) // update the validator, possibly kicking it out
logger.Info(fmt.Sprintf("Validator %s slashed by fraction %v, removed %v shares and burned %d tokens", pubkey.Address(), fraction, sharesToRemove, burned))
return
}
// revoke a validator
func (k Keeper) Revoke(ctx sdk.Context, pubkey crypto.PubKey) {
logger := ctx.Logger().With("module", "x/stake")
val, found := k.GetValidatorByPubKey(ctx, pubkey)
if !found {
panic(fmt.Errorf("Validator with pubkey %s not found, cannot revoke", pubkey))
}
val.Revoked = true
k.updateValidator(ctx, val) // update the validator, now revoked
logger.Info(fmt.Sprintf("Validator %s revoked", pubkey.Address()))
return
}
// unrevoke a validator
func (k Keeper) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) {
logger := ctx.Logger().With("module", "x/stake")
val, found := k.GetValidatorByPubKey(ctx, pubkey)
if !found {
panic(fmt.Errorf("Validator with pubkey %s not found, cannot unrevoke", pubkey))
}
val.Revoked = false
k.updateValidator(ctx, val) // update the validator, now unrevoked
logger.Info(fmt.Sprintf("Validator %s unrevoked", pubkey.Address()))
return
}

View File

@ -16,13 +16,14 @@ var (
ParamKey = []byte{0x00} // key for parameters relating to staking ParamKey = []byte{0x00} // key for parameters relating to staking
PoolKey = []byte{0x01} // key for the staking pools PoolKey = []byte{0x01} // key for the staking pools
ValidatorsKey = []byte{0x02} // prefix for each key to a validator ValidatorsKey = []byte{0x02} // prefix for each key to a validator
ValidatorsBondedKey = []byte{0x03} // prefix for each key to bonded/actively validating validators ValidatorsByPubKeyIndexKey = []byte{0x03} // prefix for each key to a validator by pubkey
ValidatorsByPowerKey = []byte{0x04} // prefix for each key to a validator sorted by power ValidatorsBondedKey = []byte{0x04} // prefix for each key to bonded/actively validating validators
ValidatorCliffKey = []byte{0x05} // key for block-local tx index ValidatorsByPowerKey = []byte{0x05} // prefix for each key to a validator sorted by power
ValidatorPowerCliffKey = []byte{0x06} // key for block-local tx index ValidatorCliffKey = []byte{0x06} // key for block-local tx index
TendermintUpdatesKey = []byte{0x07} // prefix for each key to a validator which is being updated ValidatorPowerCliffKey = []byte{0x07} // key for block-local tx index
DelegationKey = []byte{0x08} // prefix for each key to a delegator's bond TendermintUpdatesKey = []byte{0x08} // prefix for each key to a validator which is being updated
IntraTxCounterKey = []byte{0x09} // key for block-local tx index DelegationKey = []byte{0x09} // prefix for each key to a delegator's bond
IntraTxCounterKey = []byte{0x10} // key for block-local tx index
) )
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
@ -32,6 +33,11 @@ func GetValidatorKey(ownerAddr sdk.Address) []byte {
return append(ValidatorsKey, ownerAddr.Bytes()...) return append(ValidatorsKey, ownerAddr.Bytes()...)
} }
// get the key for the validator with pubkey
func GetValidatorByPubKeyIndexKey(pubkey crypto.PubKey) []byte {
return append(ValidatorsByPubKeyIndexKey, pubkey.Bytes()...)
}
// get the key for the current validator group, ordered like tendermint // get the key for the current validator group, ordered like tendermint
func GetValidatorsBondedKey(pk crypto.PubKey) []byte { func GetValidatorsBondedKey(pk crypto.PubKey) []byte {
addr := pk.Address() addr := pk.Address()

View File

@ -4,6 +4,7 @@ import (
"testing" "testing"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -463,8 +464,8 @@ func TestGetTendermintUpdatesAllNone(t *testing.T) {
updates = keeper.getTendermintUpdates(ctx) updates = keeper.getTendermintUpdates(ctx)
require.Equal(t, 2, len(updates)) require.Equal(t, 2, len(updates))
assert.Equal(t, validators[0].PubKey.Bytes(), updates[0].PubKey) assert.Equal(t, tmtypes.TM2PB.PubKey(validators[0].PubKey), updates[0].PubKey)
assert.Equal(t, validators[1].PubKey.Bytes(), updates[1].PubKey) assert.Equal(t, tmtypes.TM2PB.PubKey(validators[1].PubKey), updates[1].PubKey)
assert.Equal(t, int64(0), updates[0].Power) assert.Equal(t, int64(0), updates[0].Power)
assert.Equal(t, int64(0), updates[1].Power) assert.Equal(t, int64(0), updates[1].Power)
} }
@ -586,7 +587,7 @@ func TestGetTendermintUpdatesInserted(t *testing.T) {
func TestGetTendermintUpdatesNotValidatorCliff(t *testing.T) { func TestGetTendermintUpdatesNotValidatorCliff(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, 0) ctx, _, keeper := createTestInput(t, false, 0)
params := defaultParams() params := DefaultParams()
params.MaxValidators = 2 params.MaxValidators = 2
keeper.setParams(ctx, params) keeper.setParams(ctx, params)
@ -721,7 +722,7 @@ func TestBond(t *testing.T) {
func TestParams(t *testing.T) { func TestParams(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, 0) ctx, _, keeper := createTestInput(t, false, 0)
expParams := defaultParams() expParams := DefaultParams()
//check that the empty keeper loads the default //check that the empty keeper loads the default
resParams := keeper.GetParams(ctx) resParams := keeper.GetParams(ctx)
@ -736,7 +737,7 @@ func TestParams(t *testing.T) {
func TestPool(t *testing.T) { func TestPool(t *testing.T) {
ctx, _, keeper := createTestInput(t, false, 0) ctx, _, keeper := createTestInput(t, false, 0)
expPool := initialPool() expPool := InitialPool()
//check that the empty keeper loads the default //check that the empty keeper loads the default
resPool := keeper.GetPool(ctx) resPool := keeper.GetPool(ctx)

View File

@ -1,10 +1,7 @@
package stake package stake
import ( import (
"encoding/json"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
) )
@ -18,27 +15,21 @@ const MsgType = "stake"
const StakingToken = "steak" const StakingToken = "steak"
//Verify interface at compile time //Verify interface at compile time
var _, _, _, _ sdk.Msg = &MsgDeclareCandidacy{}, &MsgEditCandidacy{}, &MsgDelegate{}, &MsgUnbond{} var _, _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{}, &MsgUnbond{}
var msgCdc = wire.NewCodec()
func init() {
wire.RegisterCrypto(msgCdc)
}
//______________________________________________________________________ //______________________________________________________________________
// MsgDeclareCandidacy - struct for unbonding transactions // MsgCreateValidator - struct for unbonding transactions
type MsgDeclareCandidacy struct { type MsgCreateValidator struct {
Description Description
ValidatorAddr sdk.Address `json:"address"` ValidatorAddr sdk.Address `json:"address"`
PubKey crypto.PubKey `json:"pubkey"` PubKey crypto.PubKey `json:"pubkey"`
Bond sdk.Coin `json:"bond"` Bond sdk.Coin `json:"bond"`
} }
func NewMsgDeclareCandidacy(validatorAddr sdk.Address, pubkey crypto.PubKey, func NewMsgCreateValidator(validatorAddr sdk.Address, pubkey crypto.PubKey,
bond sdk.Coin, description Description) MsgDeclareCandidacy { bond sdk.Coin, description Description) MsgCreateValidator {
return MsgDeclareCandidacy{ return MsgCreateValidator{
Description: description, Description: description,
ValidatorAddr: validatorAddr, ValidatorAddr: validatorAddr,
PubKey: pubkey, PubKey: pubkey,
@ -47,18 +38,18 @@ func NewMsgDeclareCandidacy(validatorAddr sdk.Address, pubkey crypto.PubKey,
} }
//nolint //nolint
func (msg MsgDeclareCandidacy) Type() string { return MsgType } //TODO update "stake/declarecandidacy" func (msg MsgCreateValidator) Type() string { return MsgType }
func (msg MsgDeclareCandidacy) GetSigners() []sdk.Address { func (msg MsgCreateValidator) GetSigners() []sdk.Address {
return []sdk.Address{msg.ValidatorAddr} return []sdk.Address{msg.ValidatorAddr}
} }
// get the bytes for the message signer to sign on // get the bytes for the message signer to sign on
func (msg MsgDeclareCandidacy) GetSignBytes() []byte { func (msg MsgCreateValidator) GetSignBytes() []byte {
return msgCdc.MustMarshalBinary(msg) return msgCdc.MustMarshalBinary(msg)
} }
// quick validity check // quick validity check
func (msg MsgDeclareCandidacy) ValidateBasic() sdk.Error { func (msg MsgCreateValidator) ValidateBasic() sdk.Error {
if msg.ValidatorAddr == nil { if msg.ValidatorAddr == nil {
return ErrValidatorEmpty(DefaultCodespace) return ErrValidatorEmpty(DefaultCodespace)
} }
@ -77,28 +68,28 @@ func (msg MsgDeclareCandidacy) ValidateBasic() sdk.Error {
//______________________________________________________________________ //______________________________________________________________________
// MsgEditCandidacy - struct for editing a validator // MsgEditValidator - struct for editing a validator
type MsgEditCandidacy struct { type MsgEditValidator struct {
Description Description
ValidatorAddr sdk.Address `json:"address"` ValidatorAddr sdk.Address `json:"address"`
} }
func NewMsgEditCandidacy(validatorAddr sdk.Address, description Description) MsgEditCandidacy { func NewMsgEditValidator(validatorAddr sdk.Address, description Description) MsgEditValidator {
return MsgEditCandidacy{ return MsgEditValidator{
Description: description, Description: description,
ValidatorAddr: validatorAddr, ValidatorAddr: validatorAddr,
} }
} }
//nolint //nolint
func (msg MsgEditCandidacy) Type() string { return MsgType } //TODO update "stake/msgeditcandidacy" func (msg MsgEditValidator) Type() string { return MsgType }
func (msg MsgEditCandidacy) GetSigners() []sdk.Address { func (msg MsgEditValidator) GetSigners() []sdk.Address {
return []sdk.Address{msg.ValidatorAddr} return []sdk.Address{msg.ValidatorAddr}
} }
// get the bytes for the message signer to sign on // get the bytes for the message signer to sign on
func (msg MsgEditCandidacy) GetSignBytes() []byte { func (msg MsgEditValidator) GetSignBytes() []byte {
b, err := json.Marshal(msg) b, err := msgCdc.MarshalJSON(msg)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -106,7 +97,7 @@ func (msg MsgEditCandidacy) GetSignBytes() []byte {
} }
// quick validity check // quick validity check
func (msg MsgEditCandidacy) ValidateBasic() sdk.Error { func (msg MsgEditValidator) ValidateBasic() sdk.Error {
if msg.ValidatorAddr == nil { if msg.ValidatorAddr == nil {
return ErrValidatorEmpty(DefaultCodespace) return ErrValidatorEmpty(DefaultCodespace)
} }
@ -121,8 +112,8 @@ func (msg MsgEditCandidacy) ValidateBasic() sdk.Error {
// MsgDelegate - struct for bonding transactions // MsgDelegate - struct for bonding transactions
type MsgDelegate struct { type MsgDelegate struct {
DelegatorAddr sdk.Address `json:"address"` DelegatorAddr sdk.Address `json:"delegator_addr"`
ValidatorAddr sdk.Address `json:"address"` ValidatorAddr sdk.Address `json:"validator_addr"`
Bond sdk.Coin `json:"bond"` Bond sdk.Coin `json:"bond"`
} }
@ -135,14 +126,14 @@ func NewMsgDelegate(delegatorAddr, validatorAddr sdk.Address, bond sdk.Coin) Msg
} }
//nolint //nolint
func (msg MsgDelegate) Type() string { return MsgType } //TODO update "stake/msgeditcandidacy" func (msg MsgDelegate) Type() string { return MsgType }
func (msg MsgDelegate) GetSigners() []sdk.Address { func (msg MsgDelegate) GetSigners() []sdk.Address {
return []sdk.Address{msg.DelegatorAddr} return []sdk.Address{msg.DelegatorAddr}
} }
// get the bytes for the message signer to sign on // get the bytes for the message signer to sign on
func (msg MsgDelegate) GetSignBytes() []byte { func (msg MsgDelegate) GetSignBytes() []byte {
b, err := json.Marshal(msg) b, err := msgCdc.MarshalJSON(msg)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -170,8 +161,8 @@ func (msg MsgDelegate) ValidateBasic() sdk.Error {
// MsgUnbond - struct for unbonding transactions // MsgUnbond - struct for unbonding transactions
type MsgUnbond struct { type MsgUnbond struct {
DelegatorAddr sdk.Address `json:"address"` DelegatorAddr sdk.Address `json:"delegator_addr"`
ValidatorAddr sdk.Address `json:"address"` ValidatorAddr sdk.Address `json:"validator_addr"`
Shares string `json:"shares"` Shares string `json:"shares"`
} }
@ -184,12 +175,12 @@ func NewMsgUnbond(delegatorAddr, validatorAddr sdk.Address, shares string) MsgUn
} }
//nolint //nolint
func (msg MsgUnbond) Type() string { return MsgType } //TODO update "stake/msgeditcandidacy" func (msg MsgUnbond) Type() string { return MsgType }
func (msg MsgUnbond) GetSigners() []sdk.Address { return []sdk.Address{msg.DelegatorAddr} } func (msg MsgUnbond) GetSigners() []sdk.Address { return []sdk.Address{msg.DelegatorAddr} }
// get the bytes for the message signer to sign on // get the bytes for the message signer to sign on
func (msg MsgUnbond) GetSignBytes() []byte { func (msg MsgUnbond) GetSignBytes() []byte {
b, err := json.Marshal(msg) b, err := msgCdc.MarshalJSON(msg)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -18,8 +18,8 @@ var (
coinNegNotAtoms = sdk.Coin{"foo", -10000} coinNegNotAtoms = sdk.Coin{"foo", -10000}
) )
// test ValidateBasic for MsgDeclareCandidacy // test ValidateBasic for MsgCreateValidator
func TestMsgDeclareCandidacy(t *testing.T) { func TestMsgCreateValidator(t *testing.T) {
tests := []struct { tests := []struct {
name, moniker, identity, website, details string name, moniker, identity, website, details string
validatorAddr sdk.Address validatorAddr sdk.Address
@ -40,7 +40,7 @@ func TestMsgDeclareCandidacy(t *testing.T) {
for _, tc := range tests { for _, tc := range tests {
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details) description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
msg := NewMsgDeclareCandidacy(tc.validatorAddr, tc.pubkey, tc.bond, description) msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description)
if tc.expectPass { if tc.expectPass {
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else { } else {
@ -49,8 +49,8 @@ func TestMsgDeclareCandidacy(t *testing.T) {
} }
} }
// test ValidateBasic for MsgEditCandidacy // test ValidateBasic for MsgEditValidator
func TestMsgEditCandidacy(t *testing.T) { func TestMsgEditValidator(t *testing.T) {
tests := []struct { tests := []struct {
name, moniker, identity, website, details string name, moniker, identity, website, details string
validatorAddr sdk.Address validatorAddr sdk.Address
@ -64,7 +64,7 @@ func TestMsgEditCandidacy(t *testing.T) {
for _, tc := range tests { for _, tc := range tests {
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details) description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
msg := NewMsgEditCandidacy(tc.validatorAddr, description) msg := NewMsgEditValidator(tc.validatorAddr, description)
if tc.expectPass { if tc.expectPass {
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else { } else {
@ -139,8 +139,8 @@ func TestMsgUnbond(t *testing.T) {
//tests := []struct { //tests := []struct {
//tx sdk.Msg //tx sdk.Msg
//}{ //}{
//{NewMsgDeclareCandidacy(addrs[0], pks[0], bond, Description{})}, //{NewMsgCreateValidator(addrs[0], pks[0], bond, Description{})},
//{NewMsgEditCandidacy(addrs[0], Description{})}, //{NewMsgEditValidator(addrs[0], Description{})},
//{NewMsgDelegate(addrs[0], addrs[1], bond)}, //{NewMsgDelegate(addrs[0], addrs[1], bond)},
//{NewMsgUnbond(addrs[0], addrs[1], strconv.Itoa(bondAmt))}, //{NewMsgUnbond(addrs[0], addrs[1], strconv.Itoa(bondAmt))},
//} //}

View File

@ -18,12 +18,13 @@ type Params struct {
} }
func (p Params) equal(p2 Params) bool { func (p Params) equal(p2 Params) bool {
bz1 := cdcEmpty.MustMarshalBinary(&p) bz1 := msgCdc.MustMarshalBinary(&p)
bz2 := cdcEmpty.MustMarshalBinary(&p2) bz2 := msgCdc.MustMarshalBinary(&p2)
return bytes.Equal(bz1, bz2) return bytes.Equal(bz1, bz2)
} }
func defaultParams() Params { // default params
func DefaultParams() Params {
return Params{ return Params{
InflationRateChange: sdk.NewRat(13, 100), InflationRateChange: sdk.NewRat(13, 100),
InflationMax: sdk.NewRat(20, 100), InflationMax: sdk.NewRat(20, 100),

View File

@ -25,13 +25,13 @@ type Pool struct {
} }
func (p Pool) equal(p2 Pool) bool { func (p Pool) equal(p2 Pool) bool {
bz1 := cdcEmpty.MustMarshalBinary(&p) bz1 := msgCdc.MustMarshalBinary(&p)
bz2 := cdcEmpty.MustMarshalBinary(&p2) bz2 := msgCdc.MustMarshalBinary(&p2)
return bytes.Equal(bz1, bz2) return bytes.Equal(bz1, bz2)
} }
// initial pool for testing // initial pool for testing
func initialPool() Pool { func InitialPool() Pool {
return Pool{ return Pool{
LooseUnbondedTokens: 0, LooseUnbondedTokens: 0,
BondedTokens: 0, BondedTokens: 0,

View File

@ -44,8 +44,8 @@ func makeTestCodec() *wire.Codec {
cdc.RegisterInterface((*sdk.Msg)(nil), nil) cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(bank.MsgSend{}, "test/stake/Send", nil) cdc.RegisterConcrete(bank.MsgSend{}, "test/stake/Send", nil)
cdc.RegisterConcrete(bank.MsgIssue{}, "test/stake/Issue", nil) cdc.RegisterConcrete(bank.MsgIssue{}, "test/stake/Issue", nil)
cdc.RegisterConcrete(MsgDeclareCandidacy{}, "test/stake/DeclareCandidacy", nil) cdc.RegisterConcrete(MsgCreateValidator{}, "test/stake/CreateValidator", nil)
cdc.RegisterConcrete(MsgEditCandidacy{}, "test/stake/EditCandidacy", nil) cdc.RegisterConcrete(MsgEditValidator{}, "test/stake/EditValidator", nil)
cdc.RegisterConcrete(MsgUnbond{}, "test/stake/Unbond", nil) cdc.RegisterConcrete(MsgUnbond{}, "test/stake/Unbond", nil)
// Register AppAccount // Register AppAccount
@ -89,8 +89,8 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context
) )
ck := bank.NewKeeper(accountMapper) ck := bank.NewKeeper(accountMapper)
keeper := NewKeeper(cdc, keyStake, ck, DefaultCodespace) keeper := NewKeeper(cdc, keyStake, ck, DefaultCodespace)
keeper.setPool(ctx, initialPool()) keeper.setPool(ctx, InitialPool())
keeper.setNewParams(ctx, defaultParams()) keeper.setNewParams(ctx, DefaultParams())
// fill all the addresses with some coins // fill all the addresses with some coins
for _, addr := range addrs { for _, addr := range addrs {
@ -120,7 +120,7 @@ func testAddr(addr string, bech string) sdk.Address {
if err != nil { if err != nil {
panic(err) panic(err)
} }
bechexpected, err := sdk.Bech32CosmosifyAcc(res) bechexpected, err := sdk.Bech32ifyAcc(res)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -128,7 +128,7 @@ func testAddr(addr string, bech string) sdk.Address {
panic("Bech encoding doesn't match reference") panic("Bech encoding doesn't match reference")
} }
bechres, err := sdk.GetAccAddressBech32Cosmos(bech) bechres, err := sdk.GetAccAddressBech32(bech)
if err != nil { if err != nil {
panic(err) panic(err)
} }

Some files were not shown because too many files have changed in this diff Show More