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"
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:
<<: *defaults
parallelism: 4
@ -138,6 +154,9 @@ workflows:
- lint:
requires:
- setup_dependencies
- test_cli:
requires:
- setup_dependencies
- test_unit:
requires:
- setup_dependencies

View File

@ -4,3 +4,5 @@
* [ ] Updated all code comments where relevant
* [ ] Wrote tests
* [ ] 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
profile.out
### Vagrant ###
# Vagrant
.vagrant/
*.box
*.log
vagrant
# Graphviz
dependency-graph.png

View File

@ -1,30 +1,18 @@
# Changelog
## 0.18.1
BREAKING CHANGES
* [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
* Switch to bech32cosmos on all human readable inputs and outputs
FEATURES
BUG FIXES
IMPROVEMENTS
* export command now writes current validator set for Tendermint
* auto-sequencing transactions correctly
* query sequence via account store
* fixed duplicate pub_key in stake.Validator
FIXES
* [lcd] Switch to bech32 for addresses on all human readable inputs and outputs
## 0.18.0
_TBD_
_2018-06-05_
BREAKING CHANGES
@ -43,6 +31,20 @@ BREAKING CHANGES
* Introduction of Unbonding fields, lowlevel logic throughout (not fully implemented with queue)
* Introduction of PoolShares type within validators,
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
@ -56,14 +58,25 @@ FEATURES
* [stake] Creation of a validator/delegation generics in `/types`
* [stake] Helper Description of the store in x/stake/store.md
* [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
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
^ 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
* auto-sequencing transactions correctly
* query sequence via account store
* fixed duplicate pub_key in stake.Validator
* Auto-sequencing now works correctly
## 0.17.2
@ -87,7 +100,7 @@ FEATURES
* [gaiacli] Support queries for candidates, delegator-bonds
* [gaiad] Added `gaiad export` command to export current state to JSON
* [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
@ -103,6 +116,7 @@ BUG FIXES
* Auto-sequencing now works correctly
## 0.16.0 (May 14th, 2018)
BREAKING CHANGES
@ -121,7 +135,7 @@ BREAKING CHANGES
FEATURES:
* Gaia stake commands include, DeclareCandidacy, EditCandidacy, Delegate, Unbond
* Gaia stake commands include, CreateValidator, EditValidator, Delegate, Unbond
* MountStoreWithDB without providing a custom store works.
* Repo is now lint compliant / GoMetaLinter with tendermint-lint integrated into CI
* 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
## 0.15.1 (April 29, 2018)
IMPROVEMENTS:
* Update Tendermint to v0.19.1 (includes many rpc fixes)
## 0.15.0 (April 29, 2018)
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"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
revision = "1432d294a5b055c297457c25434efbf13384cc46"
revision = "86fed781132ac890ee03e906e4ecd5d6fa180c64"
[[projects]]
branch = "master"
name = "github.com/cosmos/bech32cosmos"
packages = ["go"]
revision = "efca97cd8c0852c44d96dfdcc70565c306eddff0"
name = "github.com/btcsuite/btcutil"
packages = ["bech32"]
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[projects]]
name = "github.com/davecgh/go-spew"
@ -256,7 +256,7 @@
"leveldb/table",
"leveldb/util"
]
revision = "e6d6b529196422703d54ff5c40e79809ec2020b3"
revision = "5d6fca44a948d2be89a9702de7717f0168403d3d"
[[projects]]
name = "github.com/tendermint/abci"
@ -267,8 +267,8 @@
"server",
"types"
]
revision = "78a8905690ef54f9d57e3b2b0ee7ad3a04ef3f1f"
version = "v0.10.3"
revision = "9af8b7a7c87478869f8c280ed9539470b8f470b4"
version = "v0.11.0-rc4"
[[projects]]
branch = "master"
@ -298,17 +298,14 @@
revision = "915416979bf70efa4bcbf1c6cd5d64c5fff9fc19"
version = "v0.6.2"
[[projects]]
name = "github.com/tendermint/go-wire"
packages = ["."]
revision = "fa721242b042ecd4c6ed1a934ee740db4f74e45c"
version = "v0.7.3"
[[projects]]
name = "github.com/tendermint/iavl"
packages = ["."]
revision = "fd37a0fa3a7454423233bc3d5ea828f38e0af787"
version = "v0.7.0"
packages = [
".",
"sha256truncated"
]
revision = "c9206995e8f948e99927f5084a88a7e94ca256da"
version = "v0.8.0-rc0"
[[projects]]
name = "github.com/tendermint/tendermint"
@ -319,6 +316,9 @@
"consensus",
"consensus/types",
"evidence",
"libs/events",
"libs/pubsub",
"libs/pubsub/query",
"lite",
"lite/client",
"lite/errors",
@ -347,13 +347,15 @@
"types/priv_validator",
"version"
]
revision = "018e096748bafe1d2d1e69b909e4158f3b26f6b2"
version = "v0.19.5-rc1"
revision = "b5baab0238c9ec26e3b2d229b0243f9ff9220bdb"
version = "v0.20.0-rc3"
[[projects]]
branch = "develop"
name = "github.com/tendermint/tmlibs"
packages = [
"autofile",
"bech32",
"cli",
"cli/flags",
"clist",
@ -362,11 +364,9 @@
"flowrate",
"log",
"merkle",
"pubsub",
"pubsub/query"
"merkle/tmhash"
]
revision = "cc5f287c4798ffe88c04d02df219ecb6932080fd"
version = "v0.8.3-rc0"
revision = "44f1bdb0d55cc6527e38d0a7aab406e2580f56a4"
[[projects]]
branch = "master"
@ -382,7 +382,7 @@
"ripemd160",
"salsa20/salsa"
]
revision = "1a580b3eff7814fc9b40602fd35256c63b50f491"
revision = "5ba7f63082460102a45837dbd1827e10f9479ac0"
[[projects]]
branch = "master"
@ -396,13 +396,13 @@
"internal/timeseries",
"trace"
]
revision = "57065200b4b034a1c8ad54ff77069408c2218ae6"
revision = "1e491301e022f8f977054da4c2d852decd59571f"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "7c87d13f8e835d2fb3a70a2912c811ed0c1d241b"
revision = "c11f84a56e43e20a78cee75a7c034031ecf57d1f"
[[projects]]
name = "golang.org/x/text"
@ -463,6 +463,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "f0c6224dc5f30c1a7dea716d619665831ea0932b0eb9afc6ac897dbc459134fa"
inputs-digest = "ccb2ab7644a38c2d0326280582f758256e37fc98c3ef0403581e3b85cff42188"
solver-name = "gps-cdcl"
solver-version = 1

View File

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

View File

@ -70,7 +70,7 @@ get_vendor_deps:
draw_deps:
@# requires brew install graphviz or apt-get install graphviz
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

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

View File

@ -1,7 +1,6 @@
package baseapp
import (
"bytes"
"encoding/json"
"fmt"
"os"
@ -12,6 +11,7 @@ import (
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -83,18 +83,36 @@ func TestLoadVersion(t *testing.T) {
header := abci.Header{Height: 1}
app.BeginBlock(abci.RequestBeginBlock{Header: header})
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.MountStoresIAVL(capKey)
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil
err = app.LoadLatestVersion(capKey)
assert.Nil(t, err)
testLoadVersionHelper(t, app, int64(2), commitID2)
lastHeight = app.LastBlockHeight()
lastID = app.LastCommitID()
assert.Equal(t, int64(1), lastHeight)
assert.Equal(t, commitID, lastID)
// reload with LoadVersion, see if you can commit the same block and get
// the same result
app = NewBaseApp(name, nil, logger, db)
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
@ -206,11 +224,91 @@ func TestInitChainer(t *testing.T) {
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
// on the store within a block, and that the CheckTx state
// gets reset to the latest Committed state during Commit
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
@ -224,30 +322,9 @@ func TestDeliverTx(t *testing.T) {
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
counter := 0
txPerHeight := 2
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 {
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{}
})
app.Router().AddRoute(msgType, getStateCheckingHandler(t, capKey, txPerHeight, true))
tx := testUpdatePowerTx{} // doesn't matter
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
func TestTxGasLimits(t *testing.T) {
logger := defaultLogger()
@ -510,15 +608,20 @@ func TestValidatorChange(t *testing.T) {
// Assert that validator updates are correct.
for _, val := range valSet {
pubkey, err := tmtypes.PB2TM.PubKey(val.PubKey)
// Sanity
assert.NotEqual(t, len(val.PubKey), 0)
assert.Nil(t, err)
// Find matching update and splice it out.
for j := 0; j < len(valUpdates); {
for j := 0; j < len(valUpdates); j++ {
valUpdate := valUpdates[j]
updatePubkey, err := tmtypes.PB2TM.PubKey(valUpdate.PubKey)
assert.Nil(t, err)
// Matched.
if bytes.Equal(valUpdate.PubKey, val.PubKey) {
if updatePubkey.Equals(pubkey) {
assert.Equal(t, valUpdate.Power, val.Power+1)
if j < len(valUpdates)-1 {
// Splice it out.
@ -528,7 +631,6 @@ func TestValidatorChange(t *testing.T) {
}
// Not matched.
j++
}
}
assert.Equal(t, len(valUpdates), 0, "Some validator updates were unexpected")
@ -542,7 +644,7 @@ func randPower() int64 {
func makeVal(secret string) abci.Validator {
return abci.Validator{
PubKey: makePubKey(secret).Bytes(),
PubKey: tmtypes.TM2PB.PubKey(makePubKey(secret)),
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
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()
if err != nil {
return res, err

View File

@ -102,12 +102,6 @@ func runAddCmd(cmd *cobra.Command, args []string) error {
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) {
output := viper.Get(cli.OutputFlag)
switch output {
@ -121,7 +115,10 @@ func printCreate(info keys.Info, seed string) {
fmt.Println(seed)
}
case "json":
out := addOutput{Key: info}
out, err := Bech32KeyOutput(info)
if err != nil {
panic(err)
}
if !viper.GetBool(flagNoBackup) {
out.Seed = seed
}

View File

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

View File

@ -4,7 +4,6 @@ import (
"encoding/json"
"net/http"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/gorilla/mux"
keys "github.com/tendermint/go-crypto/keys"
@ -51,7 +50,12 @@ func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
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, "", " ")
if err != nil {
w.WriteHeader(500)

View File

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

View File

@ -2,6 +2,7 @@ package lcd
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
@ -16,6 +17,7 @@ import (
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
cryptoKeys "github.com/tendermint/go-crypto/keys"
tmcfg "github.com/tendermint/tendermint/config"
nm "github.com/tendermint/tendermint/node"
@ -31,21 +33,30 @@ import (
client "github.com/cosmos/cosmos-sdk/client"
keys "github.com/cosmos/cosmos-sdk/client/keys"
bapp "github.com/cosmos/cosmos-sdk/examples/basecoin/app"
btypes "github.com/cosmos/cosmos-sdk/examples/basecoin/types"
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/server"
tests "github.com/cosmos/cosmos-sdk/tests"
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/stake"
stakerest "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
)
var (
coinDenom = "mycoin"
coinDenom = "steak"
coinAmount = int64(10000000)
validatorAddr1Hx = ""
validatorAddr2Hx = ""
validatorAddr1 = ""
validatorAddr2 = ""
// XXX bad globals
name = "test"
password = "0123456789"
port string // XXX: but it's the int ...
port string
seed string
sendAddr string
)
@ -92,13 +103,13 @@ func TestKeys(t *testing.T) {
err = cdc.UnmarshalJSON([]byte(body), &m)
require.Nil(t, err)
sendAddrAcc, _ := sdk.GetAccAddressHex(sendAddr)
addrAcc, _ := sdk.GetAccAddressHex(addr)
addrBech32, _ := sdk.Bech32ifyAcc(addrAcc)
assert.Equal(t, m[0].Name, name, "Did not serve keys name correctly")
assert.Equal(t, m[0].Address, sendAddrAcc, "Did not serve keys Address correctly")
assert.Equal(t, m[1].Name, newName, "Did not serve keys name correctly")
assert.Equal(t, m[1].Address, addrAcc, "Did not serve keys Address correctly")
assert.Equal(t, name, m[0].Name, "Did not serve keys name correctly")
assert.Equal(t, sendAddr, m[0].Address, "Did not serve keys Address correctly")
assert.Equal(t, newName, m[1].Name, "Did not serve keys name correctly")
assert.Equal(t, addrBech32, m[1].Address, "Did not serve keys Address correctly")
// select key
keyEndpoint := fmt.Sprintf("/keys/%s", newName)
@ -109,7 +120,7 @@ func TestKeys(t *testing.T) {
require.Nil(t, err)
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
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) {
var resultVals ctypes.ResultValidators
var resultVals rpc.ResultValidatorsOutput
res, body := request(t, port, "GET", "/validatorsets/latest", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
@ -199,7 +210,10 @@ func TestValidators(t *testing.T) {
err := cdc.UnmarshalJSON([]byte(body), &resultVals)
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)
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) {
bz, _ := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6")
someFakeAddr, _ := sdk.Bech32ifyAcc(bz)
// 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)
acc := getAccount(t, sendAddr)
@ -309,6 +325,62 @@ func TestTxs(t *testing.T) {
// 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
@ -324,26 +396,18 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
if err != nil {
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.Consensus.TimeoutCommit = 1000
config.Consensus.SkipTimeoutCommit = false
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
// logger = log.NewFilter(logger, log.AllowError())
logger = log.NewFilter(logger, log.AllowError())
privValidatorFile := config.PrivValidatorFile()
privVal := pvm.LoadOrGenFilePV(privValidatorFile)
db := dbm.NewMemDB()
app := bapp.NewBasecoinApp(logger, db)
cdc = bapp.MakeCodec() // XXX
app := gapp.NewGaiaApp(logger, db)
cdc = gapp.MakeCodec() // XXX
genesisFile := config.GenesisFile()
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
@ -351,25 +415,63 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
return nil, nil, err
}
coins := sdk.Coins{{coinDenom, coinAmount}}
appState := map[string]interface{}{
"accounts": []*btypes.GenesisAccount{
{
Name: "tester",
Address: pubKey.Address(),
Coins: coins,
},
genDoc.Validators = append(genDoc.Validators,
tmtypes.GenesisValidator{
PubKey: crypto.GenPrivKeyEd25519().PubKey(),
Power: 1,
Name: "val",
},
}
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 {
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
port = fmt.Sprintf("%d", 17377) // XXX
listenAddr := fmt.Sprintf("tcp://localhost:%s", port) // XXX
var listenAddr string
listenAddr, port, err = server.FreeTCPAddr()
if err != nil {
return nil, nil, err
}
// XXX: need to set this so LCD knows the tendermint node address!
viper.Set(client.FlagNode, config.RPC.ListenAddress)
@ -379,7 +481,7 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
if err != nil {
return nil, nil, err
}
lcd, err := startLCD(logger, listenAddr)
lcd, err := startLCD(logger, listenAddr, cdc)
if err != nil {
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!
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)
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()
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
require.Nil(t, err)
receiveAddr = receiveInfo.PubKey.Address().String()
receiveAddr, _ = sdk.Bech32ifyAcc(receiveInfo.PubKey.Address())
acc := getAccount(t, sendAddr)
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) {
// create receive address
kb := client.MockKeyBase()
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
require.Nil(t, err)
receiveAddr := receiveInfo.PubKey.Address().String()
receiveAddr, _ := sdk.Bech32ifyAcc(receiveInfo.PubKey.Address())
// get the account to get the sequence
acc := getAccount(t, sendAddr)
@ -494,3 +595,81 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
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"
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
ibc "github.com/cosmos/cosmos-sdk/x/ibc/client/rest"
stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest"
)
const (
@ -55,6 +56,7 @@ func startRESTServerFn(cdc *wire.Codec) func(cmd *cobra.Command, args []string)
if err != nil {
return err
}
logger.Info("REST server started")
// Wait forever and cleanup
cmn.TrapSignal(func() {
@ -83,5 +85,6 @@ func createHandler(cdc *wire.Codec) http.Handler {
auth.RegisterRoutes(ctx, r, cdc, "acc")
bank.RegisterRoutes(ctx, r, cdc, kb)
ibc.RegisterRoutes(ctx, r, cdc, kb)
stake.RegisterRoutes(ctx, r, cdc, kb)
return r
}

View File

@ -16,7 +16,8 @@ const (
flagSelect = "select"
)
func blockCommand() *cobra.Command {
//BlockCommand returns the verified block data for a given heights
func BlockCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "block [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(
initClientCommand(),
statusCommand(),
blockCommand(),
validatorCommand(),
)
}

View File

@ -10,14 +10,17 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"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
func validatorCommand() *cobra.Command {
//ValidatorCommand returns the validator set for a given height
func ValidatorCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "validatorset [height]",
Short: "Get the full validator set at given height",
Use: "validator-set [height]",
Short: "Get the full tendermint validator set at given height",
Args: cobra.MaximumNArgs(1),
RunE: printValidators,
}
@ -27,6 +30,38 @@ func validatorCommand() *cobra.Command {
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) {
// get the node
node, err := ctx.GetNode()
@ -34,12 +69,23 @@ func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) {
return nil, err
}
res, err := node.Validators(height)
validatorsRes, err := node.Validators(height)
if err != nil {
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 {
return nil, err
}
@ -95,6 +141,7 @@ func ValidatorSetRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}

View File

@ -5,6 +5,7 @@ import (
"os"
abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -15,6 +16,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
)
@ -34,10 +36,11 @@ type GaiaApp struct {
cdc *wire.Codec
// keys to access the substores
keyMain *sdk.KVStoreKey
keyAccount *sdk.KVStoreKey
keyIBC *sdk.KVStoreKey
keyStake *sdk.KVStoreKey
keyMain *sdk.KVStoreKey
keyAccount *sdk.KVStoreKey
keyIBC *sdk.KVStoreKey
keyStake *sdk.KVStoreKey
keySlashing *sdk.KVStoreKey
// Manage getting and setting accounts
accountMapper auth.AccountMapper
@ -45,6 +48,7 @@ type GaiaApp struct {
coinKeeper bank.Keeper
ibcMapper ibc.Mapper
stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
}
func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
@ -52,12 +56,13 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
// create your application object
var app = &GaiaApp{
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
cdc: cdc,
keyMain: sdk.NewKVStoreKey("main"),
keyAccount: sdk.NewKVStoreKey("acc"),
keyIBC: sdk.NewKVStoreKey("ibc"),
keyStake: sdk.NewKVStoreKey("stake"),
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
cdc: cdc,
keyMain: sdk.NewKVStoreKey("main"),
keyAccount: sdk.NewKVStoreKey("acc"),
keyIBC: sdk.NewKVStoreKey("ibc"),
keyStake: sdk.NewKVStoreKey("stake"),
keySlashing: sdk.NewKVStoreKey("slashing"),
}
// define the accountMapper
@ -71,6 +76,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
app.coinKeeper = bank.NewKeeper(app.accountMapper)
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.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace))
// register message routes
app.Router().
@ -80,9 +86,10 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
// initialize BaseApp
app.SetInitChainer(app.initChainer)
app.SetEndBlocker(stake.NewEndBlocker(app.stakeKeeper))
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.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing)
err := app.LoadLatestVersion(app.keyMain)
if err != nil {
cmn.Exit(err.Error())
@ -97,15 +104,35 @@ func MakeCodec() *wire.Codec {
ibc.RegisterWire(cdc)
bank.RegisterWire(cdc)
stake.RegisterWire(cdc)
slashing.RegisterWire(cdc)
auth.RegisterWire(cdc)
sdk.RegisterWire(cdc)
wire.RegisterCrypto(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
func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes
// TODO is this now the whole genesis file?
var genesisState 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{}
}
// export the state of gaia for a genesis f
func (app *GaiaApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
// export the state of gaia for a genesis file
func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
ctx := app.NewContext(true, abci.Header{})
// iterate to get the accounts
@ -143,5 +170,10 @@ func (app *GaiaApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
Accounts: accounts,
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
import (
"encoding/json"
"fmt"
"os"
"testing"
@ -115,7 +114,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
// Initialize the chain
vals := []abci.Validator{}
gapp.InitChain(abci.RequestInitChain{vals, stateBytes})
gapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
gapp.Commit()
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) {
logger, dbs := loggerAndDB()
gapp := NewGaiaApp(logger, dbs)
@ -178,7 +153,7 @@ func TestGenesis(t *testing.T) {
}
err = setGenesis(gapp, baseAcc)
assert.Nil(t, err)
require.Nil(t, err)
// A checkTx context
ctx := gapp.BaseApp.NewContext(true, abci.Header{})
@ -394,13 +369,13 @@ func TestStakeMsgs(t *testing.T) {
require.Equal(t, acc1, res1)
require.Equal(t, acc2, res2)
// Declare Candidacy
// Create Validator
description := stake.NewDescription("foo_moniker", "", "", "")
declareCandidacyMsg := stake.NewMsgDeclareCandidacy(
createValidatorMsg := stake.NewMsgCreateValidator(
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{})
res1 = gapp.accountMapper.GetAccount(ctxDeliver, addr1)
@ -415,13 +390,13 @@ func TestStakeMsgs(t *testing.T) {
bond, found := gapp.stakeKeeper.GetDelegation(ctxDeliver, addr1, addr1)
require.True(sdk.RatEq(t, sdk.NewRat(10), bond.Shares))
// Edit Candidacy
// Edit Validator
description = stake.NewDescription("bar_moniker", "", "", "")
editCandidacyMsg := stake.NewMsgEditCandidacy(
editValidatorMsg := stake.NewMsgEditValidator(
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)
require.True(t, found)
@ -455,6 +430,42 @@ func TestStakeMsgs(t *testing.T) {
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) {

View File

@ -65,7 +65,7 @@ func GaiaAppInit() server.AppInit {
fsAppGenState := 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,
"home directory for the client, used for key generation")
fsAppGenTx.Bool(flagOWK, false, "overwrite the accounts created")
@ -74,7 +74,7 @@ func GaiaAppInit() server.AppInit {
FlagsAppGenState: fsAppGenState,
FlagsAppGenTx: fsAppGenTx,
AppGenTx: GaiaAppGenTx,
AppGenState: GaiaAppGenState,
AppGenState: GaiaAppGenStateJSON,
}
}
@ -85,19 +85,35 @@ type GaiaGenTx struct {
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) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
var addr sdk.Address
var secret string
clientRoot := viper.GetString(flagClientHome)
overwrite := viper.GetBool(flagOWK)
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)
if err != nil {
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
gaiaGenTx := GaiaGenTx{
@ -111,13 +127,6 @@ func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey) (
}
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{
PubKey: pk,
Power: freeFermionVal,
@ -127,7 +136,7 @@ func GaiaAppGenTx(cdc *wire.Codec, pk crypto.PubKey) (
// Create the core parameters for genesis initialization for gaia
// 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 {
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
genesisState := GenesisState{
genesisState = GenesisState{
Accounts: genaccs,
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)
return
}

View File

@ -30,46 +30,44 @@ func TestGaiaCLISend(t *testing.T) {
executeWrite(t, "gaiacli keys add bar", pass)
// 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)
// start gaiad server
cmd, _, _ := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr))
defer cmd.Process.Kill()
proc := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr))
defer proc.Stop(false)
tests.WaitForStart(port)
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")
barCech, err := sdk.Bech32ifyAcc(barAddr)
require.NoError(t, err)
fooBech, err := sdk.Bech32CosmosifyAcc(fooAddr)
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))
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooCech, flags))
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)
time.Sleep(time.Second * 3) // waiting for some blocks to pass
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), 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"))
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"))
// test autosequencing
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barAddr), pass)
time.Sleep(time.Second * 3) // waiting for some blocks to pass
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), 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"))
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"))
}
func TestGaiaCLIDeclareCandidacy(t *testing.T) {
func TestGaiaCLICreateValidator(t *testing.T) {
tests.ExecuteT(t, "gaiad unsafe_reset_all")
pass := "1234567890"
@ -79,95 +77,79 @@ func TestGaiaCLIDeclareCandidacy(t *testing.T) {
executeWrite(t, "gaiacli keys add bar", pass)
// 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)
// start gaiad server
cmd, _, _ := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr))
defer cmd.Process.Kill()
proc := tests.GoExecuteT(t, fmt.Sprintf("gaiad start --rpc.laddr=%v", servAddr))
defer proc.Stop(false)
tests.WaitForStart(port)
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)
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)
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
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooBech, flags))
assert.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak"))
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"))
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooCech, flags))
assert.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak"))
// declare candidacy
declStr := fmt.Sprintf("gaiacli create-validator %v", flags)
declStr += fmt.Sprintf(" --name=%v", "bar")
declStr += fmt.Sprintf(" --validator-address=%v", bechVal)
declStr += fmt.Sprintf(" --amount=%v", "3steak")
declStr += fmt.Sprintf(" --moniker=%v", "bar-vally")
fmt.Printf("debug declStr: %v\n", declStr)
executeWrite(t, declStr, pass)
time.Sleep(time.Second) // waiting for some blocks to pass
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags))
assert.Equal(t, int64(7), barAcc.GetCoins().AmountOf("steak"))
candidate := executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, barAddr))
assert.Equal(t, candidate.Owner.String(), barAddr)
assert.Equal(t, int64(3), candidate.PoolShares)
// create validator
cvStr := fmt.Sprintf("gaiacli stake create-validator %v", flags)
cvStr += fmt.Sprintf(" --name=%v", "bar")
cvStr += fmt.Sprintf(" --address-validator=%v", barCech)
cvStr += fmt.Sprintf(" --pubkey=%v", barCeshPubKey)
cvStr += fmt.Sprintf(" --amount=%v", "2steak")
cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally")
executeWrite(t, cvStr, pass)
time.Sleep(time.Second * 3) // waiting for some blocks to pass
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
require.Equal(t, int64(8), barAcc.GetCoins().AmountOf("steak"), "%v", barAcc)
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
//unbondStr := fmt.Sprintf("gaiacli unbond %v", flags)
//unbondStr += fmt.Sprintf(" --name=%v", "bar")
//unbondStr += fmt.Sprintf(" --address-candidate=%v", barAddr)
//unbondStr += fmt.Sprintf(" --address-delegator=%v", barAddr)
//unbondStr += fmt.Sprintf(" --shares=%v", "1")
//unbondStr += fmt.Sprintf(" --sequence=%v", "1")
//fmt.Printf("debug unbondStr: %v\n", unbondStr)
//executeWrite(t, unbondStr, pass)
//time.Sleep(time.Second * 3) // waiting for some blocks to pass
//barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags))
//assert.Equal(t, int64(99998), barAcc.GetCoins().AmountOf("steak"))
//candidate = executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, barAddr))
//assert.Equal(t, int64(2), candidate.BondedShares.Evaluate())
unbondStr := fmt.Sprintf("gaiacli stake unbond %v", flags)
unbondStr += fmt.Sprintf(" --name=%v", "bar")
unbondStr += fmt.Sprintf(" --address-validator=%v", barCech)
unbondStr += fmt.Sprintf(" --address-delegator=%v", barCech)
unbondStr += fmt.Sprintf(" --shares=%v", "1")
unbondStr += fmt.Sprintf(" --sequence=%v", "1")
t.Log(fmt.Sprintf("debug unbondStr: %v\n", unbondStr))
executeWrite(t, unbondStr, pass)
time.Sleep(time.Second * 3) // waiting for some blocks to pass
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags))
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) {
cmd, wc, _ := tests.GoExecuteT(t, cmdStr)
proc := tests.GoExecuteT(t, cmdStr)
for _, write := range writes {
_, err := wc.Write([]byte(write + "\n"))
if err != nil {
fmt.Println(err)
}
_, err := proc.StdinPipe.Write([]byte(write + "\n"))
require.NoError(t, err)
}
fmt.Printf("debug waiting cmdStr: %v\n", cmdStr)
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))
proc.Wait()
}
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)
var ko keys.KeyOutput
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 {
@ -204,11 +193,11 @@ func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
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)
var candidate stake.Validator
var validator stake.Validator
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &candidate)
require.NoError(t, err, "out %v, err %v", out, err)
return candidate
err := cdc.UnmarshalJSON([]byte(out), &validator)
require.NoError(t, err, "out %v\n, err %v", out, err)
return validator
}

View File

@ -14,6 +14,7 @@ import (
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/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"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
@ -35,36 +36,83 @@ func main() {
// the below functions and eliminate global vars, like we do
// with the cdc
// add standard rpc, and tx commands
// add standard rpc commands
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(
advancedCmd,
client.LineBreak,
)
//Add stake commands
stakeCmd := &cobra.Command{
Use: "stake",
Short: "Stake and validation subcommands",
}
stakeCmd.AddCommand(
client.GetCommands(
authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)),
stakecmd.GetCmdQueryValidator("stake", cdc),
stakecmd.GetCmdQueryValidators("stake", cdc),
stakecmd.GetCmdQueryDelegation("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(
client.PostCommands(
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
rootCmd.AddCommand(
client.LineBreak,
lcd.ServeCommand(cdc),
keys.Commands(),
client.LineBreak,
version.VersionCmd,

View File

@ -6,6 +6,7 @@ import (
"github.com/spf13/cobra"
abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -17,6 +18,7 @@ import (
func main() {
cdc := app.MakeCodec()
ctx := server.NewDefaultContext()
cobra.EnableCommandSorting = false
rootCmd := &cobra.Command{
Use: "gaiad",
Short: "Gaia Daemon (server)",
@ -25,7 +27,7 @@ func main() {
server.AddCommands(ctx, cdc, rootCmd, app.GaiaAppInit(),
server.ConstructAppCreator(newApp, "gaia"),
server.ConstructAppExporter(exportAppState, "gaia"))
server.ConstructAppExporter(exportAppStateAndTMValidators, "gaia"))
// prepare and add flags
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)
}
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)
return gapp.ExportAppStateJSON()
return gapp.ExportAppStateAndValidators()
}

View File

@ -2,9 +2,9 @@ swagger: '2.0'
info:
version: '1.1.0'
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:
kms:
type: basic
@ -58,7 +58,7 @@ paths:
responses:
200:
description: '"true" or "false"'
/keys:
get:
summary: List of accounts stored locally
@ -102,7 +102,7 @@ paths:
- application/json
responses:
200:
description: 12 word Seed
description: 16 word Seed
schema:
type: string
/keys/{name}:
@ -199,12 +199,12 @@ paths:
# description: Tx was send and will probably be added to the next block
# 400:
# description: The Tx was malformated
/accounts/{address}:
parameters:
- in: path
name: address
description: Account address
description: Account address in bech32 format
required: true
type: string
get:
@ -222,7 +222,7 @@ paths:
parameters:
- in: path
name: address
description: Account address
description: Account address in bech32 format
required: true
type: string
post:
@ -255,18 +255,6 @@ paths:
description: Tx was send and will probably be added to the next block
400:
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:
get:
summary: Get the latest block
@ -304,9 +292,14 @@ paths:
200:
description: The validator set at the latest block height
schema:
type: array
items:
$ref: "#/definitions/Delegate"
type: object
properties:
block_height:
type: number
validators:
type: array
items:
$ref: "#/definitions/Validator"
/validatorsets/{height}:
parameters:
- in: path
@ -322,9 +315,14 @@ paths:
200:
description: The validator set at a specific block height
schema:
type: array
items:
$ref: "#/definitions/Delegate"
type: object
properties:
block_height:
type: number
validators:
type: array
items:
$ref: "#/definitions/Validator"
404:
description: Block at height not available
# /txs:
@ -545,11 +543,24 @@ paths:
# description: Tx was send and will probably be added to the next block
# 400:
# description: The Tx was malformated
definitions:
Address:
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:
type: object
properties:
@ -630,7 +641,7 @@ definitions:
Sig:
type: string
default: ''
Pubkey:
Pubkey:
type: string
default: ''
TxSigned:
@ -652,16 +663,6 @@ definitions:
example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958
Pubkey:
$ref: "#/definitions/PubKey"
PubKey:
type: object
properties:
type:
type: string
enum:
- ed25519
data:
type: string
example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958
Account:
type: object
properties:
@ -704,17 +705,17 @@ definitions:
properties:
header:
type: object
properties:
properties:
chain_id:
type: string
example: gaia-2
height:
height:
type: number
example: 1
time:
time:
type: string
example: '2017-12-30T05:53:09.287+01:00'
num_txs:
num_txs:
type: number
example: 0
last_block_id:
@ -753,17 +754,19 @@ definitions:
type: array
items:
type: object
Delegate:
Validator:
type: object
properties:
address:
$ref: '#/definitions/ValidatorAddress'
pub_key:
$ref: "#/definitions/PubKey"
$ref: "#/definitions/ValidatorPubKey"
power:
type: number
example: 1000
name:
type: string
example: "159.89.3.34"
accum:
type: number
example: 1000
# Added by API Auto Mocking Plugin
host: virtserver.swaggerhub.com
basePath: /faboweb1/Cosmos-LCD-2/1.0.0

View File

@ -203,7 +203,7 @@ where the ``--sequence`` flag is to be incremented for each transaction, the ``-
::
Please enter passphrase for alice:
Please enter passphrase for alice:
{
"check_tx": {
"gas": 30
@ -250,7 +250,7 @@ First, we need the pub_key data:
::
cat $HOME/.gaia2/priv_validator.json
cat $HOME/.gaia2/priv_validator.json
the first part will look like:
@ -260,17 +260,17 @@ the first part will look like:
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:
::
Please enter passphrase for bob:
Please enter passphrase for bob:
{
"check_tx": {
"gas": 30
@ -285,7 +285,7 @@ We should see ``bob``'s account balance decrease by 10 mycoin:
::
gaiacli account 5D93A6059B6592833CBC8FA3DA90EE0382198985
gaiacli account 5D93A6059B6592833CBC8FA3DA90EE0382198985
To confirm for certain the new validator is active, ask the tendermint node:
@ -306,19 +306,19 @@ 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``:
::
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:
::
Please enter passphrase for charlie:
Please enter passphrase for charlie:
{
"check_tx": {
"gas": 30
@ -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:
@ -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:
@ -383,9 +383,9 @@ with an output similar to:
"Shares": 20
}
}
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
@ -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
See the bond decrease with ``gaiacli query delegator-bond`` like above.
See the bond decrease with ``gaiacli stake delegation`` like above.

View File

@ -16,7 +16,7 @@ First, generate a couple of genesis transactions to be incorparated into the gen
gaiacli keys list
**Note:** If you've already run these tests you may need to overwrite keys using the ``--OWK`` flag
When you list the keys you should see two addresses, we'll need these later so take note.
When you list the keys you should see two addresses, we'll need these later so take note.
Now let's actually create the genesis files for both nodes:
::
@ -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:
@ -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!

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -14,6 +15,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
@ -29,10 +31,11 @@ type BasecoinApp struct {
cdc *wire.Codec
// keys to access the substores
keyMain *sdk.KVStoreKey
keyAccount *sdk.KVStoreKey
keyIBC *sdk.KVStoreKey
keyStake *sdk.KVStoreKey
keyMain *sdk.KVStoreKey
keyAccount *sdk.KVStoreKey
keyIBC *sdk.KVStoreKey
keyStake *sdk.KVStoreKey
keySlashing *sdk.KVStoreKey
// Manage getting and setting accounts
accountMapper auth.AccountMapper
@ -40,6 +43,7 @@ type BasecoinApp struct {
coinKeeper bank.Keeper
ibcMapper ibc.Mapper
stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
}
func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
@ -49,12 +53,13 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
// Create your application object.
var app = &BasecoinApp{
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
cdc: cdc,
keyMain: sdk.NewKVStoreKey("main"),
keyAccount: sdk.NewKVStoreKey("acc"),
keyIBC: sdk.NewKVStoreKey("ibc"),
keyStake: sdk.NewKVStoreKey("stake"),
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
cdc: cdc,
keyMain: sdk.NewKVStoreKey("main"),
keyAccount: sdk.NewKVStoreKey("acc"),
keyIBC: sdk.NewKVStoreKey("ibc"),
keyStake: sdk.NewKVStoreKey("stake"),
keySlashing: sdk.NewKVStoreKey("slashing"),
}
// Define the accountMapper.
@ -68,6 +73,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
app.coinKeeper = bank.NewKeeper(app.accountMapper)
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.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace))
// register message routes
app.Router().
@ -78,8 +84,10 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
// Initialize BaseApp.
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.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing)
err := app.LoadLatestVersion(app.keyMain)
if err != nil {
cmn.Exit(err.Error())
@ -94,6 +102,7 @@ func MakeCodec() *wire.Codec {
sdk.RegisterWire(cdc) // Register Msgs
bank.RegisterWire(cdc)
stake.RegisterWire(cdc)
slashing.RegisterWire(cdc)
ibc.RegisterWire(cdc)
// register custom AppAccount
@ -102,6 +111,24 @@ func MakeCodec() *wire.Codec {
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
func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes
@ -121,11 +148,15 @@ func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain)
}
app.accountMapper.SetAccount(ctx, acc)
}
// load the initial stake information
stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
return abci.ResponseInitChain{}
}
// 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{})
// iterate to get the accounts
@ -143,5 +174,10 @@ func (app *BasecoinApp) ExportAppStateJSON() (appState json.RawMessage, err erro
genState := types.GenesisState{
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"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/stake"
abci "github.com/tendermint/abci/types"
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) {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
db := dbm.NewMemDB()
@ -96,33 +122,11 @@ func newBasecoinApp() *BasecoinApp {
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) {
bapp := newBasecoinApp()
require.Nil(t, setGenesis(bapp))
msgs := []struct {
msg sdk.Msg
@ -161,7 +165,7 @@ func TestSortGenesis(t *testing.T) {
// Initialize the chain
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, []byte(genState)})
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: []byte(genState)})
bapp.Commit()
// Unsorted coins means invalid
@ -193,8 +197,8 @@ func TestGenesis(t *testing.T) {
}
acc := &types.AppAccount{baseAcc, "foobart"}
err = setGenesisAccounts(bapp, baseAcc)
assert.Nil(t, err)
err = setGenesis(bapp, baseAcc)
require.Nil(t, err)
// A checkTx context
ctx := bapp.BaseApp.NewContext(true, abci.Header{})
@ -222,8 +226,9 @@ func TestMsgChangePubKey(t *testing.T) {
}
// Construct genesis state
err = setGenesisAccounts(bapp, baseAcc)
assert.Nil(t, err)
err = setGenesis(bapp, baseAcc)
require.Nil(t, err)
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
@ -276,8 +281,9 @@ func TestMsgSendWithAccounts(t *testing.T) {
}
// Construct genesis state
err = setGenesisAccounts(bapp, baseAcc)
assert.Nil(t, err)
err = setGenesis(bapp, baseAcc)
require.Nil(t, err)
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
@ -320,8 +326,9 @@ func TestMsgSendMultipleOut(t *testing.T) {
Coins: genCoins,
}
err = setGenesisAccounts(bapp, acc1, acc2)
assert.Nil(t, err)
// Construct genesis state
err = setGenesis(bapp, acc1, acc2)
require.Nil(t, err)
// Simulate a Block
SignCheckDeliver(t, bapp, sendMsg2, []int64{0}, true, priv1)
@ -353,7 +360,7 @@ func TestSengMsgMultipleInOut(t *testing.T) {
Coins: genCoins,
}
err = setGenesisAccounts(bapp, acc1, acc2, acc4)
err = setGenesis(bapp, acc1, acc2, acc4)
assert.Nil(t, err)
// CheckDeliver
@ -377,7 +384,11 @@ func TestMsgSendDependent(t *testing.T) {
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)
// CheckDeliver
@ -416,7 +427,7 @@ func TestMsgQuiz(t *testing.T) {
// Initialize the chain (nil)
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit()
// A checkTx context (true)
@ -438,8 +449,9 @@ func TestIBCMsgs(t *testing.T) {
}
acc1 := &types.AppAccount{baseAcc, "foobart"}
err := setGenesisAccounts(bapp, baseAcc)
err := setGenesis(bapp, baseAcc)
assert.Nil(t, err)
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)

View File

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

View File

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

View File

@ -4,6 +4,7 @@ import (
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/stake"
)
var _ auth.Account = (*AppAccount)(nil)
@ -41,7 +42,8 @@ func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder {
// State to Unmarshal
type GenesisState struct {
Accounts []*GenesisAccount `json:"accounts"`
Accounts []*GenesisAccount `json:"accounts"`
StakeData stake.GenesisState `json:"stake"`
}
// GenesisAccount doesn't need pubkey or sequence

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -154,7 +155,7 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep
}
// 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{})
// iterate to get the accounts
@ -174,5 +175,9 @@ func (app *DemocoinApp) ExportAppStateJSON() (appState json.RawMessage, err erro
POWGenesis: pow.WriteGenesis(ctx, app.powKeeper),
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")
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit()
// A checkTx context
@ -184,7 +184,7 @@ func TestMsgSendWithAccounts(t *testing.T) {
// Initialize the chain
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit()
// A checkTx context (true)
@ -262,7 +262,7 @@ func TestMsgMine(t *testing.T) {
// Initialize the chain (nil)
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit()
// A checkTx context (true)
@ -309,7 +309,7 @@ func TestMsgQuiz(t *testing.T) {
// Initialize the chain (nil)
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit()
// A checkTx context (true)
@ -356,7 +356,7 @@ func TestHandler(t *testing.T) {
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
bapp.Commit()
// A checkTx context (true)

View File

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

View File

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

View File

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

View File

@ -26,7 +26,7 @@ func NewMsgBond(addr sdk.Address, stake sdk.Coin, pubKey crypto.PubKey) MsgBond
}
//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} }
// basic validation of the bond message
@ -65,7 +65,7 @@ func NewMsgUnbond(addr sdk.Address) MsgUnbond {
}
//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) ValidateBasic() sdk.Error { return nil }

View File

@ -5,6 +5,7 @@ import (
"path/filepath"
abci "github.com/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
)
@ -13,8 +14,8 @@ import (
// and other flags (?) to start
type AppCreator func(string, log.Logger) (abci.Application, error)
// AppExporter dumps all app state to JSON-serializable structure
type AppExporter func(home string, log log.Logger) (json.RawMessage, error)
// 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, []tmtypes.GenesisValidator, error)
// ConstructAppCreator returns an application generation function
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
func ConstructAppExporter(appFn func(log.Logger, dbm.DB) (json.RawMessage, error), name string) AppExporter {
return func(rootDir string, logger log.Logger) (json.RawMessage, error) {
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, []tmtypes.GenesisValidator, error) {
dataDir := filepath.Join(rootDir, "data")
db, err := dbm.NewGoLevelDB(name, dataDir)
if err != nil {
return nil, err
return nil, nil, err
}
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",
RunE: func(cmd *cobra.Command, args []string) error {
home := viper.GetString("home")
appState, err := appExporter(home, ctx.Logger)
appState, validators, err := appExporter(home, ctx.Logger)
if err != nil {
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
}
doc.AppStateJSON = appState
doc.Validators = validators
encoded, err := wire.MarshalJSONIndent(cdc, doc)
if err != nil {
return err

View File

@ -37,7 +37,9 @@ func TestStartStandAlone(t *testing.T) {
app, err := mock.NewApp(home, logger)
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")
svr.SetLogger(logger.With("module", "abci-server"))
svr.Start()
@ -69,7 +71,9 @@ func TestStartWithTendermint(t *testing.T) {
// set up app and start up
viper.Set(flagWithTendermint, true)
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
close(RunOrTimeout(startCmd, timeout, t))

View File

@ -16,14 +16,17 @@ import (
// Get a free address for a test tendermint server
// 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")
defer l.Close()
require.Nil(t, err)
if err != nil {
return "", "", err
}
port := l.Addr().(*net.TCPAddr).Port
addr := fmt.Sprintf("tcp://0.0.0.0:%d", port)
return addr
portI := l.Addr().(*net.TCPAddr).Port
port = fmt.Sprintf("%d", portI)
addr = fmt.Sprintf("tcp://0.0.0.0:%s", port)
return
}
// setupViper creates a homedir to run inside,

View File

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

View File

@ -9,6 +9,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/wire"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
@ -72,13 +73,24 @@ func AddCommands(
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(
InitCmd(ctx, cdc, appInit),
StartCmd(ctx, appCreator),
UnsafeResetAllCmd(ctx),
ShowNodeIDCmd(ctx),
ShowValidatorCmd(ctx),
client.LineBreak,
tendermintCmd,
ExportCmd(ctx, cdc, appExport),
client.LineBreak,
version.VersionCmd,
)
}

View File

@ -1,51 +1,91 @@
package tests
import (
"io"
"os/exec"
"strings"
"testing"
"time"
"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 := strings.Split(command, " ")
// Split cmd to name and args.
split := strings.Split(cmd, " ")
require.True(t, len(split) > 0, "no command provided")
var cmd *exec.Cmd
if len(split) == 1 {
cmd = exec.Command(split[0])
} else {
cmd = exec.Command(split[0], split[1:]...)
name, args := split[0], []string(nil)
if len(split) > 1 {
args = split[1:]
}
return cmd
}
// Execute the command, return standard output and error, try a few times if requested
func ExecuteT(t *testing.T, command string) (out string) {
cmd := getCmd(t, command)
bz, err := cmd.CombinedOutput()
if err != nil {
panic(err)
// Start process and wait.
proc, err := StartProcess("", name, args, nil, nil)
require.NoError(t, err)
proc.Wait()
// 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))
out = strings.Trim(string(bz), "\n") //trim any new lines
time.Sleep(time.Second)
if len(errbz) > 0 {
t.Log("Stderr:", cmn.Red(string(errbz)))
}
// Collect STDOUT output.
out = strings.Trim(string(outbz), "\n") //trim any new lines
return out
}
// Asynchronously execute the command, return standard output and error
func GoExecuteT(t *testing.T, command string) (cmd *exec.Cmd, pipeIn io.WriteCloser, pipeOut io.ReadCloser) {
cmd = getCmd(t, command)
pipeIn, err := cmd.StdinPipe()
// Execute the command, launch goroutines to log stdout/err to t.
// Caller should wait for .Wait() or .Stop() to terminate.
func GoExecuteT(t *testing.T, cmd string) (proc *Process) {
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)
pipeOut, err = cmd.StdoutPipe()
require.NoError(t, err)
cmd.Start()
time.Sleep(time.Second)
return cmd, pipeIn, pipeOut
// Run goroutines to log stdout.
go func() {
buf := make([]byte, 10240) // TODO Document the effects.
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/filepath"
"strings"
//"strings"
"testing"
"time"
@ -239,7 +238,9 @@ func StartNodeServerForTest(t *testing.T, home string) *exec.Cmd {
// expects TestInitBaseCoin to have been run
func StartLCDServerForTest(t *testing.T, home, chainID string) (cmd *exec.Cmd, port string) {
cmdName := whereIsBasecli()
port = strings.Split(server.FreeTCPAddr(t), ":")[2]
var err error
_, port, err = server.FreeTCPAddr()
require.NoError(t, err)
cmdArgs := []string{
"rest-server",
"--home",
@ -252,7 +253,7 @@ func StartLCDServerForTest(t *testing.T, home, chainID string) (cmd *exec.Cmd, p
cmd = exec.Command(cmdName, cmdArgs...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
err = cmd.Start()
require.Nil(t, err)
time.Sleep(time.Second * 2) // TODO: LOL
return cmd, port

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@ package types
import (
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-crypto"
tmtypes "github.com/tendermint/tendermint/types"
)
// status of a validator
@ -31,6 +32,7 @@ func BondStatusToString(b BondStatus) string {
// validator for a delegated proof of stake system
type Validator interface {
GetMoniker() string // moniker of the validator
GetStatus() BondStatus // status of the validator
GetOwner() Address // owner address to receive/return validators coins
GetPubKey() crypto.PubKey // validation pubkey
@ -41,7 +43,7 @@ type Validator interface {
// validator which fulfills abci validator interface for use in Tendermint
func ABCIValidator(v Validator) abci.Validator {
return abci.Validator{
PubKey: v.GetPubKey().Bytes(),
PubKey: tmtypes.TM2PB.PubKey(v.GetPubKey()),
Power: v.GetPower().Evaluate(),
}
}
@ -56,8 +58,11 @@ type ValidatorSet interface {
IterateValidatorsBonded(Context,
func(index int64, validator Validator) (stop bool))
Validator(Context, Address) Validator // get a particular validator by owner address
TotalPower(Context) Rat // total power of the validator set
Validator(Context, Address) Validator // get a particular validator by owner address
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...)
}
// 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
func NewTags(tags ...interface{}) 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)
//__________________________________________________________

View File

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

View File

@ -1,7 +1,6 @@
package rest
import (
"encoding/hex"
"fmt"
"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 {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
addr := vars["address"]
bech32addr := vars["address"]
bz, err := hex.DecodeString(addr)
addr, err := sdk.GetAccAddressBech32(bech32addr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
key := sdk.Address(bz)
res, err := ctx.Query(key, storeName)
res, err := ctx.Query(addr, storeName)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error())))

View File

@ -1,8 +1,6 @@
package auth
import (
"encoding/json"
sdk "github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
)
@ -30,7 +28,7 @@ func (msg MsgChangeKey) ValidateBasic() sdk.Error {
// Implements Msg.
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 {
panic(err)
}

View File

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

View File

@ -10,3 +10,10 @@ func RegisterWire(cdc *wire.Codec) {
cdc.RegisterConcrete(&BaseAccount{}, "auth/Account", 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)
to, err := sdk.GetAccAddressBech32Cosmos(toStr)
to, err := sdk.GetAccAddressBech32(toStr)
if err != nil {
return err
}

View File

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

View File

@ -1,8 +1,6 @@
package bank
import (
"encoding/json"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -55,7 +53,7 @@ func (msg MsgSend) ValidateBasic() sdk.Error {
// Implements Msg.
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 {
panic(err)
}
@ -104,7 +102,7 @@ func (msg MsgIssue) ValidateBasic() sdk.Error {
// Implements Msg.
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 {
panic(err)
}

View File

@ -186,8 +186,14 @@ func TestMsgSendGetSignBytes(t *testing.T) {
Outputs: []Output{NewOutput(addr2, coins)},
}
res := msg.GetSignBytes()
unmarshaledMsg := &MsgSend{}
msgCdc.UnmarshalJSON(res, unmarshaledMsg)
assert.Equal(t, &msg, unmarshaledMsg)
// 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) {
@ -255,8 +261,14 @@ func TestMsgIssueGetSignBytes(t *testing.T) {
Outputs: []Output{NewOutput(addr, coins)},
}
res := msg.GetSignBytes()
unmarshaledMsg := &MsgIssue{}
msgCdc.UnmarshalJSON(res, unmarshaledMsg)
assert.Equal(t, &msg, unmarshaledMsg)
// 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) {

View File

@ -9,3 +9,9 @@ func RegisterWire(cdc *wire.Codec) {
cdc.RegisterConcrete(MsgSend{}, "cosmos-sdk/Send", 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
vars := mux.Vars(r)
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
body, err := ioutil.ReadAll(r.Body)
@ -58,7 +65,7 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.Core
return
}
bz, err := hex.DecodeString(address)
bz, err := hex.DecodeString(address.String())
if err != nil {
w.WriteHeader(http.StatusBadRequest)
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),
RunE: func(cmd *cobra.Command, args []string) error {
addr, err := sdk.GetAccAddressBech32Cosmos(args[0])
addr, err := sdk.GetAccAddressBech32(args[0])
if err != nil {
return err
}
@ -72,25 +72,25 @@ func GetCmdQueryValidators(storeName string, cdc *wire.Codec) *cobra.Command {
return err
}
// parse out the candidates
var candidates []stake.Validator
// parse out the validators
var validators []stake.Validator
for _, KV := range resKVs {
var validator stake.Validator
cdc.MustUnmarshalBinary(KV.Value, &validator)
candidates = append(candidates, validator)
validators = append(validators, validator)
}
switch viper.Get(cli.OutputFlag) {
case "text":
for _, candidate := range candidates {
resp, err := candidate.HumanReadableString()
for _, validator := range validators {
resp, err := validator.HumanReadableString()
if err != nil {
return err
}
fmt.Println(resp)
}
case "json":
output, err := wire.MarshalJSONIndent(cdc, candidates)
output, err := wire.MarshalJSONIndent(cdc, validators)
if err != nil {
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",
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 {
return err
}
@ -157,7 +157,7 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
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 {
cmd := &cobra.Command{
Use: "delegations [delegator-addr]",
@ -165,7 +165,7 @@ func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
delegatorAddr, err := sdk.GetAccAddressBech32Cosmos(args[0])
delegatorAddr, err := sdk.GetAccAddressBech32(args[0])
if err != nil {
return err
}
@ -176,7 +176,7 @@ func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
return err
}
// parse out the candidates
// parse out the validators
var delegations []stake.Delegation
for _, KV := range resKVs {
var delegation stake.Delegation

View File

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

View File

@ -1,12 +1,10 @@
package rest
import (
"encoding/hex"
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/tendermint/go-crypto/keys"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -14,34 +12,39 @@ import (
"github.com/cosmos/cosmos-sdk/x/stake"
)
// RegisterRoutes - Central function to define routes that get registered by the main application
func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) {
r.HandleFunc("/stake/{delegator}/bonding_status/{validator}", BondingStatusHandlerFn("stake", cdc, kb, ctx)).Methods("GET")
func registerQueryRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) {
r.HandleFunc(
"/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
func BondingStatusHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase, ctx context.CoreContext) http.HandlerFunc {
// http request handler to query delegator bonding status
func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// read parameters
vars := mux.Vars(r)
delegator := vars["delegator"]
validator := vars["validator"]
bech32delegator := vars["delegator"]
bech32validator := vars["validator"]
bz, err := hex.DecodeString(delegator)
delegatorAddr, err := sdk.GetAccAddressBech32(bech32delegator)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
delegatorAddr := sdk.Address(bz)
bz, err = hex.DecodeString(validator)
validatorAddr, err := sdk.GetValAddressBech32(bech32validator)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
validatorAddr := sdk.Address(bz)
key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc)
@ -76,3 +79,103 @@ func BondingStatusHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase,
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
func (b Delegation) HumanReadableString() (string, error) {
bechAcc, err := sdk.Bech32CosmosifyAcc(b.DelegatorAddr)
bechAcc, err := sdk.Bech32ifyAcc(b.DelegatorAddr)
if err != nil {
return "", err
}
bechVal, err := sdk.Bech32CosmosifyAcc(b.ValidatorAddr)
bechVal, err := sdk.Bech32ifyAcc(b.ValidatorAddr)
if err != nil {
return "", err
}

View File

@ -16,6 +16,7 @@ const (
CodeInvalidValidator CodeType = 201
CodeInvalidBond CodeType = 202
CodeInvalidInput CodeType = 203
CodeValidatorJailed CodeType = 204
CodeUnauthorized CodeType = sdk.CodeUnauthorized
CodeInternal CodeType = sdk.CodeInternal
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")
}
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 {
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 {
return newError(codespace, CodeInvalidValidator, "Missing signature")

View File

@ -1,6 +1,10 @@
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
type GenesisState struct {
@ -22,21 +26,32 @@ func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []D
// get raw genesis raw message for testing
func DefaultGenesisState() GenesisState {
return GenesisState{
Pool: initialPool(),
Params: defaultParams(),
Pool: InitialPool(),
Params: DefaultParams(),
}
}
// InitGenesis - store genesis parameters
func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
store := ctx.KVStore(k.storeKey)
k.setPool(ctx, data.Pool)
k.setNewParams(ctx, data.Params)
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 {
k.setDelegation(ctx, bond)
}
k.updateBondedValidatorsFull(ctx, store)
}
// WriteGenesis - output genesis parameters
@ -52,3 +67,16 @@ func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
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 {
// NOTE msg already has validate basic run
switch msg := msg.(type) {
case MsgDeclareCandidacy:
return handleMsgDeclareCandidacy(ctx, msg, k)
case MsgEditCandidacy:
return handleMsgEditCandidacy(ctx, msg, k)
case MsgCreateValidator:
return handleMsgCreateValidator(ctx, msg, k)
case MsgEditValidator:
return handleMsgEditValidator(ctx, msg, k)
case MsgDelegate:
return handleMsgDelegate(ctx, msg, k)
case MsgUnbond:
@ -25,13 +25,27 @@ func NewHandler(k Keeper) sdk.Handler {
}
}
// NewEndBlocker generates sdk.EndBlocker
// Performs tick functionality
func NewEndBlocker(k Keeper) sdk.EndBlocker {
return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
res.ValidatorUpdates = k.Tick(ctx)
return
// Called every block, process inflation, update validator set
func EndBlocker(ctx sdk.Context, k Keeper) (ValidatorUpdates []abci.Validator) {
pool := k.GetPool(ctx)
// Process Validator Provisions
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,
// 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
_, 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)
k.setValidator(ctx, validator)
k.setValidatorByPubKeyIndex(ctx, validator)
tags := sdk.NewTags(
"action", []byte("declareCandidacy"),
"action", []byte("createValidator"),
"validator", msg.ValidatorAddr.Bytes(),
"moniker", []byte(msg.Description.Moniker),
"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, found := k.GetValidator(ctx, msg.ValidatorAddr)
@ -94,7 +109,7 @@ func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk
k.updateValidator(ctx, validator)
tags := sdk.NewTags(
"action", []byte("editCandidacy"),
"action", []byte("editValidator"),
"validator", msg.ValidatorAddr.Bytes(),
"moniker", []byte(msg.Description.Moniker),
"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)
// remove the bond
revokeCandidacy := false
revokeValidator := false
if bond.Shares.IsZero() {
// 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) &&
validator.Revoked == false {
revokeCandidacy = true
revokeValidator = true
}
k.removeDelegation(ctx, bond)
@ -232,7 +247,7 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
/////////////////////////////////////
// revoke validator if necessary
if revokeCandidacy {
if revokeValidator {
validator.Revoked = true
}

View File

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

View File

@ -2,7 +2,6 @@ package stake
import (
sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/abci/types"
)
const (
@ -12,30 +11,6 @@ const (
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
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/x/bank"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
)
// 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)
}
// 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)
func (k Keeper) getValidator(store sdk.KVStore, addr sdk.Address) (validator Validator, found bool) {
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
func (k Keeper) setValidator(ctx sdk.Context, validator Validator) {
store := ctx.KVStore(k.storeKey)
// set main store
bz := k.cdc.MustMarshalBinary(validator)
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
func (k Keeper) getAllValidators(ctx sdk.Context) (validators Validators) {
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)
if validator.Revoked && oldValidator.Status() == sdk.Bonded {
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
k.setPool(ctx, pool)
validator = k.unbondValidator(ctx, store, validator)
// 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
@ -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
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)
// 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
store.Delete(GetValidatorsBondedKey(validator.PubKey))
return validator
}
// 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)
pool := k.getPool(store)
store.Delete(GetValidatorKey(address))
store.Delete(GetValidatorByPubKeyIndexKey(validator.PubKey))
store.Delete(GetValidatorsByPowerKey(validator, pool))
// 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
}
func (k Keeper) setPool(ctx sdk.Context, p Pool) {
func (k Keeper) setPool(ctx sdk.Context, pool Pool) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(p)
b := k.cdc.MustMarshalBinary(pool)
store.Set(PoolKey, b)
}
@ -656,6 +685,13 @@ func (k Keeper) setCliffValidator(ctx sdk.Context, validator Validator, pool Poo
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
@ -749,3 +785,46 @@ func (k Keeper) IterateDelegators(ctx sdk.Context, delAddr sdk.Address, fn func(
}
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

@ -13,16 +13,17 @@ import (
//nolint
var (
// Keys for store prefixes
ParamKey = []byte{0x00} // key for parameters relating to staking
PoolKey = []byte{0x01} // key for the staking pools
ValidatorsKey = []byte{0x02} // prefix for each key to a validator
ValidatorsBondedKey = []byte{0x03} // prefix for each key to bonded/actively validating validators
ValidatorsByPowerKey = []byte{0x04} // prefix for each key to a validator sorted by power
ValidatorCliffKey = []byte{0x05} // key for block-local tx index
ValidatorPowerCliffKey = []byte{0x06} // key for block-local tx index
TendermintUpdatesKey = []byte{0x07} // prefix for each key to a validator which is being updated
DelegationKey = []byte{0x08} // prefix for each key to a delegator's bond
IntraTxCounterKey = []byte{0x09} // key for block-local tx index
ParamKey = []byte{0x00} // key for parameters relating to staking
PoolKey = []byte{0x01} // key for the staking pools
ValidatorsKey = []byte{0x02} // prefix for each key to a validator
ValidatorsByPubKeyIndexKey = []byte{0x03} // prefix for each key to a validator by pubkey
ValidatorsBondedKey = []byte{0x04} // prefix for each key to bonded/actively validating validators
ValidatorsByPowerKey = []byte{0x05} // prefix for each key to a validator sorted by power
ValidatorCliffKey = []byte{0x06} // key for block-local tx index
ValidatorPowerCliffKey = []byte{0x07} // key for block-local tx index
TendermintUpdatesKey = []byte{0x08} // prefix for each key to a validator which is being updated
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
@ -32,6 +33,11 @@ func GetValidatorKey(ownerAddr sdk.Address) []byte {
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
func GetValidatorsBondedKey(pk crypto.PubKey) []byte {
addr := pk.Address()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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