Merge branch 'develop' into feature/ibc

This commit is contained in:
Ethan Buchman 2018-05-07 09:42:40 -04:00 committed by GitHub
commit ffed398035
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
205 changed files with 6387 additions and 3673 deletions

View File

@ -27,11 +27,17 @@ jobs:
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run:
name: linter
command: |
export PATH="$GOBIN:$PATH"
go get -u github.com/tendermint/lint/golint
go get -u github.com/alecthomas/gometalinter
- run:
name: binaries
command: |
export PATH="$GOBIN:$PATH"
make build
make install
- persist_to_workspace:
root: /tmp/workspace
paths:
@ -46,6 +52,54 @@ jobs:
paths:
- /go/src/github.com/cosmos/cosmos-sdk
lint:
<<: *defaults
parallelism: 4
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Lint source
command: |
export PATH="$GOBIN:$PATH"
gometalinter --disable-all --enable='golint' --vendor ./...
test_unit:
<<: *defaults
parallelism: 4
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Test unit
command: |
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
@ -59,6 +113,8 @@ jobs:
- run:
name: Run tests
command: |
export PATH="$GOBIN:$PATH"
make install
for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v /vendor/ | circleci tests split --split-by=timings); do
id=$(basename "$pkg")
@ -94,10 +150,18 @@ workflows:
test-suite:
jobs:
- setup_dependencies
- lint:
requires:
- setup_dependencies
- test_unit:
requires:
- setup_dependencies
- test_cli:
requires:
- setup_dependencies
- test_cover:
requires:
- setup_dependencies
- upload_coverage:
requires:
- test_cover

View File

@ -1,12 +1,50 @@
# Changelog
## 0.15.0 (TBD)
## 0.16.0 (TBD)
BREAKING CHANGES
* Move module REST/CLI packages to x/[module]/client/rest and x/[module]/client/cli
* Gaia simple-staking bond and unbond functions replaced
* [stake] Delegator bonds now store the height at which they were updated
* All module keepers now require a codespace, see basecoin or democoin for usage
* Many changes to names throughout
* Type as a prefix naming convention applied (ex. BondMsg -> MsgBond)
* Removed redundancy in names (ex. stake.StakeKeeper -> stake.Keeper)
* Removed SealedAccountMapper
* gaiad init now requires use of `--name` flag
* Removed Get from Msg interface
* types/rational now extends big.Rat
FEATURES:
* Add CacheContext
* Add auto sequencing to client
* Add FeeHandler to ante handler
* Gaia stake commands include, DeclareCandidacy, EditCandidacy, 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
* gaiad init overhaul
* Create genesis transactions with `gaiad init gen-tx`
* New genesis account keys are automatically added to the client keybase (introduce `--client-home` flag)
* Initialize with genesis txs using `--gen-txs` flag
* Context now has access to the application-configured logger
BUG FIXES
* Gaia now uses stake, ported from github.com/cosmos/gaia
* Auto-sequencing now works correctly
## 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
[Amino](github.com/tendermint/go-amino).
For details on how this changes encoding for public keys and addresses,
see the [docs](https://github.com/tendermint/tendermint/blob/v0.19.1/docs/specification/new-spec/encoding.md#public-key-cryptography).
BREAKING CHANGES
@ -14,8 +52,13 @@ BREAKING CHANGES
* [store] Add `SubspaceIterator` and `ReverseSubspaceIterator` to `KVStore` interface
* [basecoin] NewBasecoinApp takes a `dbm.DB` and uses namespaced DBs for substores
BUG FIXES
FEATURES:
* Add CacheContext
* Add auto sequencing to client
* Add FeeHandler to ante handler
BUG FIXES
* MountStoreWithDB without providing a custom store works.
## 0.14.1 (April 9, 2018)
@ -63,7 +106,7 @@ BREAKING CHANGES
to allow mounting multiple stores with their own DB until they can share one
* [x/staking] Renamed to `simplestake`
* [builder] Functions don't take `passphrase` as argument
* [server] GenAppState returns generated seed and address
* [server] GenAppParams returns generated seed and address
* [basecoind] `init` command outputs JSON of everything necessary for testnet
* [basecoind] `basecoin.db -> data/basecoin.db`
* [basecli] `data/keys.db -> keys/keys.db`

25
Gopkg.lock generated
View File

@ -11,7 +11,7 @@
branch = "master"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
revision = "2be2f12b358dc57d70b8f501b00be450192efbc3"
revision = "675abc5df3c5531bc741b56a765e35623459da6d"
[[projects]]
name = "github.com/davecgh/go-spew"
@ -277,8 +277,8 @@
[[projects]]
name = "github.com/tendermint/go-amino"
packages = ["."]
revision = "42246108ff925a457fb709475070a03dfd3e2b5c"
version = "0.9.6"
revision = "ed62928576cfcaf887209dc96142cd79cdfff389"
version = "0.9.9"
[[projects]]
name = "github.com/tendermint/go-crypto"
@ -342,8 +342,8 @@
"types/priv_validator",
"version"
]
revision = "d0beaba7e8a5652506a34b5fab299cc2dc274c02"
version = "v0.19.0"
revision = "26f633ed48441f72895b710f0e87b7b6c6791066"
version = "v0.19.1"
[[projects]]
name = "github.com/tendermint/tmlibs"
@ -360,8 +360,8 @@
"pubsub",
"pubsub/query"
]
revision = "737154202faf75c70437f59ba5303f2eb09f5636"
version = "0.8.2-rc1"
revision = "d94e312673e16a11ea55d742cefb3e331228f898"
version = "v0.8.2"
[[projects]]
branch = "master"
@ -377,13 +377,14 @@
"ripemd160",
"salsa20/salsa"
]
revision = "d6449816ce06963d9d136eee5a56fca5b0616e7e"
revision = "b49d69b5da943f7ef3c9cf91c8777c1f78a0cc3c"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
@ -391,13 +392,13 @@
"lex/httplex",
"trace"
]
revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
revision = "5f9ae10d9af5b1c89ae6904293b14b064d4ada23"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
revision = "cbbc999da32df943dac6cd71eb3ee39e1d7838b9"
[[projects]]
name = "golang.org/x/text"
@ -424,7 +425,7 @@
branch = "master"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "51d0944304c3cbce4afe9e5247e21100037bff78"
revision = "86e600f69ee4704c6efbf6a2a40a5c10700e76c2"
[[projects]]
name = "google.golang.org/grpc"
@ -459,6 +460,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "b6b2d696a242e715ddb8b25c93b3c8f7e7cabc9292eab29dffe935eddbd35fb8"
inputs-digest = "fad966346d3b6042faf2bf793168b6ce9a8ff59ae207b7ad57008ead0f3ff7d4"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -62,7 +62,7 @@
[[constraint]]
name = "github.com/tendermint/go-amino"
version = "~0.9.6"
version = "~0.9.9"
[[constraint]]
name = "github.com/tendermint/iavl"
@ -70,7 +70,7 @@
[[constraint]]
name = "github.com/tendermint/tendermint"
version = "0.19.0"
version = "0.19.1"
[[override]]
name = "github.com/tendermint/tmlibs"

View File

@ -1,13 +1,14 @@
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
PACKAGES_NOCLITEST=$(shell go list ./... | grep -v '/vendor/' | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test)
COMMIT_HASH := $(shell git rev-parse --short HEAD)
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}"
all: check_tools get_vendor_deps build build_examples test
all: check_tools get_vendor_deps install install_examples test_lint test
########################################
### CI
ci: get_tools get_vendor_deps build test_cover
ci: get_tools get_vendor_deps install test_cover test_lint test
########################################
### Build
@ -15,11 +16,11 @@ ci: get_tools get_vendor_deps build test_cover
# This can be unified later, here for easy demos
build:
ifeq ($(OS),Windows_NT)
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaiad
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaiacli
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli
else
go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaiad
go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaiacli
go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaia/cmd/gaiad
go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaia/cmd/gaiacli
endif
build_examples:
@ -36,8 +37,8 @@ else
endif
install:
go install $(BUILD_FLAGS) ./cmd/gaiad
go install $(BUILD_FLAGS) ./cmd/gaiacli
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli
install_examples:
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecoind
@ -83,21 +84,22 @@ godocs:
########################################
### Testing
test: test_unit # test_cli
test: test_unit test_cli
# Must be run in each package seperately for the visualization
# Added here for easy reference
# coverage:
# go test -coverprofile=c.out && go tool cover -html=c.out
test_cli:
@go test -count 1 -p 1 `go list github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test`
test_unit:
@go test $(PACKAGES)
@go test $(PACKAGES_NOCLITEST)
test_cover:
@bash tests/test_cover.sh
test_lint:
gometalinter --disable-all --enable='golint' --vendor ./...
benchmark:
@go test -bench=. $(PACKAGES)
@go test -bench=. $(PACKAGES_NOCLITEST)
########################################
@ -127,4 +129,4 @@ devdoc_update:
# To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: build build_examples install install_examples dist check_tools get_tools get_vendor_deps draw_deps test test_unit test_tutorial benchmark devdoc_init devdoc devdoc_save devdoc_update
.PHONY: build build_examples install install_examples dist check_tools get_tools get_vendor_deps draw_deps test test_cli test_unit test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update

View File

@ -13,6 +13,7 @@ import (
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
// Key to store the header in the DB itself.
@ -24,11 +25,12 @@ var dbHeaderKey = []byte("header")
// The ABCI application
type BaseApp struct {
// initialized on creation
Logger log.Logger
name string // application name from abci.Info
db dbm.DB // common DB backend
cms sdk.CommitMultiStore // Main (uncached) state
router Router // handle any kind of message
Logger log.Logger
name string // application name from abci.Info
db dbm.DB // common DB backend
cms sdk.CommitMultiStore // Main (uncached) state
router Router // handle any kind of message
codespacer *sdk.Codespacer // handle module codespacing
// must be set
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
@ -55,14 +57,20 @@ var _ abci.Application = (*BaseApp)(nil)
// Create and name new BaseApp
// NOTE: The db is used to store the version number for now.
func NewBaseApp(name string, logger log.Logger, db dbm.DB) *BaseApp {
return &BaseApp{
Logger: logger,
name: name,
db: db,
cms: store.NewCommitMultiStore(db),
router: NewRouter(),
func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *BaseApp {
app := &BaseApp{
Logger: logger,
name: name,
db: db,
cms: store.NewCommitMultiStore(db),
router: NewRouter(),
codespacer: sdk.NewCodespacer(),
txDecoder: defaultTxDecoder(cdc),
}
// Register the undefined & root codespaces, which should not be used by any modules
app.codespacer.RegisterOrPanic(sdk.CodespaceUndefined)
app.codespacer.RegisterOrPanic(sdk.CodespaceRoot)
return app
}
// BaseApp Name
@ -70,6 +78,11 @@ func (app *BaseApp) Name() string {
return app.name
}
// Register the next available codespace through the baseapp's codespacer, starting from a default
func (app *BaseApp) RegisterCodespace(codespace sdk.CodespaceType) sdk.CodespaceType {
return app.codespacer.RegisterNext(codespace)
}
// Mount a store to the provided key in the BaseApp multistore
func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) {
for _, key := range keys {
@ -87,10 +100,31 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) {
app.cms.MountStoreWithDB(key, typ, nil)
}
// nolint - Set functions
// Set the txDecoder function
func (app *BaseApp) SetTxDecoder(txDecoder sdk.TxDecoder) {
app.txDecoder = txDecoder
}
// default custom logic for transaction decoding
func defaultTxDecoder(cdc *wire.Codec) sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx = sdk.StdTx{}
if len(txBytes) == 0 {
return nil, sdk.ErrTxDecode("txBytes are empty")
}
// StdTx.Msg is an interface. The concrete types
// are registered by MakeTxCodec
err := cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode("").Trace(err.Error())
}
return tx, nil
}
}
// nolint - Set functions
func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) {
app.initChainer = initChainer
}
@ -103,7 +137,6 @@ func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) {
func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) {
app.anteHandler = ah
}
func (app *BaseApp) Router() Router { return app.router }
// load latest application version
@ -177,9 +210,9 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error {
// NewContext returns a new Context with the correct store, the given header, and nil txBytes.
func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context {
if isCheckTx {
return sdk.NewContext(app.checkState.ms, header, true, nil)
return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger)
}
return sdk.NewContext(app.deliverState.ms, header, false, nil)
return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger)
}
type state struct {
@ -195,7 +228,7 @@ func (app *BaseApp) setCheckState(header abci.Header) {
ms := app.cms.CacheMultiStore()
app.checkState = &state{
ms: ms,
ctx: sdk.NewContext(ms, header, true, nil),
ctx: sdk.NewContext(ms, header, true, nil, app.Logger),
}
}
@ -203,11 +236,12 @@ func (app *BaseApp) setDeliverState(header abci.Header) {
ms := app.cms.CacheMultiStore()
app.deliverState = &state{
ms: ms,
ctx: sdk.NewContext(ms, header, false, nil),
ctx: sdk.NewContext(ms, header, false, nil, app.Logger),
}
}
//----------------------------------------
//______________________________________________________________________________
// ABCI
// Implements ABCI
@ -231,7 +265,6 @@ func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOp
// InitChain runs the initialization logic directly on the CommitMultiStore and commits it.
func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) {
if app.initChainer == nil {
// TODO: should we have some default handling of validators?
return
}
@ -241,7 +274,6 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
// NOTE: we don't commit, but BeginBlock for block 1
// starts from this deliverState
return
}
@ -311,9 +343,8 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
if result.IsOK() {
app.valUpdates = append(app.valUpdates, result.ValidatorUpdates...)
} else {
// Even though the Code is not OK, there will be some side
// effects, like those caused by fee deductions or sequence
// incrementations.
// Even though the Result.Code is not OK, there are still effects,
// namely fee deductions and sequence incrementing.
}
// Tell the blockchain engine (i.e. Tendermint).
@ -327,7 +358,7 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
}
}
// Mostly for testing
// nolint- Mostly for testing
func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) {
return app.runTx(true, nil, tx)
}
@ -355,6 +386,7 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
// Validate the Msg.
err := msg.ValidateBasic()
if err != nil {
err = err.WithDefaultCodespace(sdk.CodespaceRoot)
return err.Result()
}
@ -441,7 +473,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
// Use the header from this latest block.
app.setCheckState(header)
// Emtpy the Deliver state
// Empty the Deliver state
app.deliverState = nil
return abci.ResponseCommit{

View File

@ -25,7 +25,7 @@ func defaultLogger() log.Logger {
func newBaseApp(name string) *BaseApp {
logger := defaultLogger()
db := dbm.NewMemDB()
return NewBaseApp(name, logger, db)
return NewBaseApp(name, nil, logger, db)
}
func TestMountStores(t *testing.T) {
@ -59,7 +59,7 @@ func TestLoadVersion(t *testing.T) {
logger := defaultLogger()
db := dbm.NewMemDB()
name := t.Name()
app := NewBaseApp(name, logger, db)
app := NewBaseApp(name, nil, logger, db)
// make a cap key and mount the store
capKey := sdk.NewKVStoreKey("main")
@ -81,7 +81,7 @@ func TestLoadVersion(t *testing.T) {
commitID := sdk.CommitID{1, res.Data}
// reload
app = NewBaseApp(name, logger, db)
app = NewBaseApp(name, nil, logger, db)
app.MountStoresIAVL(capKey)
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
@ -147,7 +147,7 @@ func TestInitChainer(t *testing.T) {
name := t.Name()
db := dbm.NewMemDB()
logger := defaultLogger()
app := NewBaseApp(name, logger, db)
app := NewBaseApp(name, nil, logger, db)
// make cap keys and mount the stores
// NOTE/TODO: mounting multiple stores is broken
// see https://github.com/cosmos/cosmos-sdk/issues/532
@ -184,7 +184,7 @@ func TestInitChainer(t *testing.T) {
assert.Equal(t, value, res.Value)
// reload app
app = NewBaseApp(name, logger, db)
app = NewBaseApp(name, nil, logger, db)
app.MountStoresIAVL(capKey, capKey2)
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
@ -240,7 +240,7 @@ func TestDeliverTx(t *testing.T) {
height := int64((counter / txPerHeight) + 1)
assert.Equal(t, height, thisHeader.Height)
counter += 1
counter++
return sdk.Result{}
})
@ -318,13 +318,12 @@ type testUpdatePowerTx struct {
const msgType = "testUpdatePowerTx"
func (tx testUpdatePowerTx) Type() string { return msgType }
func (tx testUpdatePowerTx) Get(key interface{}) (value interface{}) { return nil }
func (tx testUpdatePowerTx) GetMsg() sdk.Msg { return tx }
func (tx testUpdatePowerTx) GetSignBytes() []byte { return nil }
func (tx testUpdatePowerTx) ValidateBasic() sdk.Error { return nil }
func (tx testUpdatePowerTx) GetSigners() []sdk.Address { return nil }
func (tx testUpdatePowerTx) GetSignatures() []sdk.StdSignature { return nil }
func (tx testUpdatePowerTx) Type() string { return msgType }
func (tx testUpdatePowerTx) GetMsg() sdk.Msg { return tx }
func (tx testUpdatePowerTx) GetSignBytes() []byte { return nil }
func (tx testUpdatePowerTx) ValidateBasic() sdk.Error { return nil }
func (tx testUpdatePowerTx) GetSigners() []sdk.Address { return nil }
func (tx testUpdatePowerTx) GetSignatures() []sdk.StdSignature { return nil }
func TestValidatorChange(t *testing.T) {

View File

@ -1,4 +1,4 @@
package core
package context
import (
"fmt"
@ -126,7 +126,14 @@ func (ctx CoreContext) SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *w
}
// sign and build the transaction from the msg
func (ctx CoreContext) SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (*ctypes.ResultBroadcastTxCommit, error) {
func (ctx CoreContext) EnsureSignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (res *ctypes.ResultBroadcastTxCommit, err error) {
// default to next sequence number if none provided
ctx, err = EnsureSequence(ctx)
if err != nil {
return nil, err
}
passphrase, err := ctx.GetPassphraseFromStdin(name)
if err != nil {
return nil, err
@ -141,17 +148,22 @@ func (ctx CoreContext) SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Co
}
// get the next sequence for the account address
func (c CoreContext) NextSequence(address []byte) (int64, error) {
if c.Decoder == nil {
func (ctx CoreContext) NextSequence(address []byte) (int64, error) {
if ctx.Decoder == nil {
return 0, errors.New("AccountDecoder required but not provided")
}
res, err := c.Query(address, c.AccountStore)
res, err := ctx.Query(address, ctx.AccountStore)
if err != nil {
return 0, err
}
account, err := c.Decoder(res)
if len(res) == 0 {
fmt.Printf("No account found, defaulting to sequence 0\n")
return 0, err
}
account, err := ctx.Decoder(res)
if err != nil {
panic(err)
}

View File

@ -1,4 +1,4 @@
package core
package context
import (
rpcclient "github.com/tendermint/tendermint/rpc/client"
@ -6,6 +6,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// typical context created in sdk modules for transactions/queries
type CoreContext struct {
ChainID string
Height int64

View File

@ -2,6 +2,7 @@ package context
import (
"fmt"
"github.com/spf13/viper"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
@ -9,11 +10,10 @@ import (
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/core"
)
// NewCoreContextFromViper - return a new context with parameters from the command line
func NewCoreContextFromViper() core.CoreContext {
func NewCoreContextFromViper() CoreContext {
nodeURI := viper.GetString(client.FlagNode)
var rpc rpcclient.Client
if nodeURI != "" {
@ -27,7 +27,7 @@ func NewCoreContextFromViper() core.CoreContext {
chainID = def
}
}
return core.CoreContext{
return CoreContext{
ChainID: chainID,
Height: viper.GetInt64(client.FlagHeight),
TrustNode: viper.GetBool(client.FlagTrustNode),
@ -36,7 +36,7 @@ func NewCoreContextFromViper() core.CoreContext {
Sequence: viper.GetInt64(client.FlagSequence),
Client: rpc,
Decoder: nil,
AccountStore: "main",
AccountStore: "acc",
}
}
@ -54,8 +54,9 @@ func defaultChainID() (string, error) {
}
// EnsureSequence - automatically set sequence number if none provided
func EnsureSequence(ctx core.CoreContext) (core.CoreContext, error) {
if viper.IsSet(client.FlagSequence) {
func EnsureSequence(ctx CoreContext) (CoreContext, error) {
// Should be viper.IsSet, but this does not work - https://github.com/spf13/viper/pull/331
if viper.GetInt64(client.FlagSequence) != 0 {
return ctx, nil
}
from, err := ctx.GetFromAddress()

View File

@ -135,14 +135,17 @@ func printCreate(info keys.Info, seed string) {
}
}
/////////////////////////////
// REST
// new key request REST body
type NewKeyBody struct {
Name string `json:"name"`
Password string `json:"password"`
Seed string `json:"seed"`
}
// add new key REST handler
func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
var kb keys.Keybase
var m NewKeyBody
@ -208,6 +211,7 @@ func getSeed(algo keys.CryptoAlgo) string {
return seed
}
// Seed REST request handler
func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
algoType := vars["type"]

View File

@ -7,7 +7,6 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/gorilla/mux"
"github.com/pkg/errors"
keys "github.com/tendermint/go-crypto/keys"
"github.com/spf13/cobra"
@ -18,14 +17,12 @@ func deleteKeyCommand() *cobra.Command {
Use: "delete <name>",
Short: "Delete the given key",
RunE: runDeleteCmd,
Args: cobra.ExactArgs(1),
}
return cmd
}
func runDeleteCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
buf := client.BufferStdin()
@ -48,12 +45,15 @@ func runDeleteCmd(cmd *cobra.Command, args []string) error {
return nil
}
////////////////////////
// REST
// delete key request REST body
type DeleteKeyBody struct {
Password string `json:"password"`
}
// delete key REST handler
func DeleteKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["name"]

View File

@ -31,8 +31,10 @@ func runListCmd(cmd *cobra.Command, args []string) error {
return err
}
//REST
/////////////////////////
// REST
// query key list REST handler
func QueryKeysRequestHandler(w http.ResponseWriter, r *http.Request) {
kb, err := GetKeyBase()
if err != nil {

View File

@ -29,6 +29,7 @@ func Commands() *cobra.Command {
return cmd
}
// resgister REST routes
func RegisterRoutes(r *mux.Router) {
r.HandleFunc("/keys", QueryKeysRequestHandler).Methods("GET")
r.HandleFunc("/keys", AddNewKeyRequestHandler).Methods("POST")

View File

@ -5,7 +5,6 @@ import (
"net/http"
"github.com/gorilla/mux"
"github.com/pkg/errors"
keys "github.com/tendermint/go-crypto/keys"
"github.com/spf13/cobra"
@ -15,7 +14,15 @@ var showKeysCmd = &cobra.Command{
Use: "show <name>",
Short: "Show key info for the given name",
Long: `Return public details of one local key.`,
RunE: runShowCmd,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]
info, err := getKey(name)
if err == nil {
printInfo(info)
}
return err
},
}
func getKey(name string) (keys.Info, error) {
@ -27,23 +34,10 @@ func getKey(name string) (keys.Info, error) {
return kb.Get(name)
}
// CMD
func runShowCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
info, err := getKey(name)
if err == nil {
printInfo(info)
}
return err
}
///////////////////////////
// REST
// get key REST handler
func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["name"]

View File

@ -7,7 +7,6 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/gorilla/mux"
"github.com/pkg/errors"
keys "github.com/tendermint/go-crypto/keys"
"github.com/spf13/cobra"
@ -18,14 +17,12 @@ func updateKeyCommand() *cobra.Command {
Use: "update <name>",
Short: "Change the password used to protect private key",
RunE: runUpdateCmd,
Args: cobra.ExactArgs(1),
}
return cmd
}
func runUpdateCmd(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a name for the key")
}
name := args[0]
buf := client.BufferStdin()
@ -53,13 +50,16 @@ func runUpdateCmd(cmd *cobra.Command, args []string) error {
return nil
}
///////////////////////
// REST
// update key request REST body
type UpdateKeyBody struct {
NewPassword string `json:"new_password"`
OldPassword string `json:"old_password"`
}
// update key REST handler
func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["name"]

View File

@ -1,8 +1,11 @@
package keys
import (
"encoding/hex"
"encoding/json"
"fmt"
"path/filepath"
"strings"
"github.com/spf13/viper"
@ -16,23 +19,18 @@ import (
// KeyDBName is the directory under root where we store the keys
const KeyDBName = "keys"
var (
// keybase is used to make GetKeyBase a singleton
keybase keys.Keybase
)
// keybase is used to make GetKeyBase a singleton
var keybase keys.Keybase
// used for outputting keys.Info over REST
type KeyOutput struct {
Name string `json:"name"`
Address string `json:"address"`
// TODO add pubkey?
// Pubkey string `json:"pubkey"`
// initialize a keybase based on the configuration
func GetKeyBase() (keys.Keybase, error) {
rootDir := viper.GetString(cli.HomeFlag)
return GetKeyBaseFromDir(rootDir)
}
// GetKeyBase initializes a keybase based on the configuration
func GetKeyBase() (keys.Keybase, error) {
// initialize a keybase based on the configuration
func GetKeyBaseFromDir(rootDir string) (keys.Keybase, error) {
if keybase == nil {
rootDir := viper.GetString(cli.HomeFlag)
db, err := dbm.NewGoLevelDB(KeyDBName, filepath.Join(rootDir, "keys"))
if err != nil {
return nil, err
@ -47,36 +45,57 @@ func SetKeyBase(kb keys.Keybase) {
keybase = kb
}
// used for outputting keys.Info over REST
type KeyOutput struct {
Name string `json:"name"`
Address string `json:"address"`
PubKey string `json:"pub_key"`
}
func NewKeyOutput(info keys.Info) KeyOutput {
return KeyOutput{
Name: info.Name,
Address: info.PubKey.Address().String(),
PubKey: strings.ToUpper(hex.EncodeToString(info.PubKey.Bytes())),
}
}
func NewKeyOutputs(infos []keys.Info) []KeyOutput {
kos := make([]KeyOutput, len(infos))
for i, info := range infos {
kos[i] = NewKeyOutput(info)
}
return kos
}
func printInfo(info keys.Info) {
ko := NewKeyOutput(info)
switch viper.Get(cli.OutputFlag) {
case "text":
addr := info.PubKey.Address().String()
sep := "\t\t"
if len(info.Name) > 7 {
sep = "\t"
}
fmt.Printf("%s%s%s\n", info.Name, sep, addr)
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\tPUBKEY:\n")
fmt.Printf("%s\t%s\t%s\n", ko.Name, ko.Address, ko.PubKey)
case "json":
json, err := MarshalJSON(info)
out, err := json.MarshalIndent(ko, "", "\t")
if err != nil {
panic(err) // really shouldn't happen...
panic(err)
}
fmt.Println(string(json))
fmt.Println(string(out))
}
}
func printInfos(infos []keys.Info) {
kos := NewKeyOutputs(infos)
switch viper.Get(cli.OutputFlag) {
case "text":
fmt.Println("All keys:")
for _, i := range infos {
printInfo(i)
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\tPUBKEY:\n")
for _, ko := range kos {
fmt.Printf("%s\t%s\t%s\n", ko.Name, ko.Address, ko.PubKey)
}
case "json":
json, err := MarshalJSON(infos)
out, err := json.MarshalIndent(kos, "", "\t")
if err != nil {
panic(err) // really shouldn't happen...
panic(err)
}
fmt.Println(string(json))
fmt.Println(string(out))
}
}

View File

@ -11,6 +11,12 @@ func init() {
wire.RegisterCrypto(cdc)
}
// marshal keys
func MarshalJSON(o interface{}) ([]byte, error) {
return cdc.MarshalJSON(o)
}
// unmarshal json
func UnmarshalJSON(bz []byte, ptr interface{}) error {
return cdc.UnmarshalJSON(bz, ptr)
}

View File

@ -7,34 +7,14 @@ import (
"os"
"path/filepath"
"strings"
"time"
cmn "github.com/tendermint/tmlibs/common"
cfg "github.com/tendermint/tendermint/config"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
)
var globalConfig *cfg.Config
func waitForRPC() {
laddr := GetConfig().RPC.ListenAddress
fmt.Println("LADDR", laddr)
client := rpcclient.NewJSONRPCClient(laddr)
ctypes.RegisterAmino(client.Codec())
result := new(ctypes.ResultStatus)
for {
_, err := client.Call("status", map[string]interface{}{}, result)
if err == nil {
return
} else {
fmt.Println("error", err)
time.Sleep(time.Millisecond)
}
}
}
// f**ing long, but unique for each test
func makePathname() string {
// get path

View File

@ -10,7 +10,6 @@ import (
"os"
"regexp"
"testing"
"time"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
@ -34,6 +33,7 @@ import (
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"
tests "github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -42,9 +42,9 @@ var (
coinAmount = int64(10000000)
// XXX bad globals
name = "test"
password = "0123456789"
port string // XXX: but it's the int ...
name string = "test"
password string = "0123456789"
seed string
sendAddr string
)
@ -80,7 +80,7 @@ func TestKeys(t *testing.T) {
jsonStr = []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed": "%s"}`, newName, newPassword, newSeed))
res, body = request(t, port, "POST", "/keys", jsonStr)
assert.Equal(t, http.StatusOK, res.StatusCode, body)
require.Equal(t, http.StatusOK, res.StatusCode, body)
addr := body
assert.Len(t, addr, 40, "Returned address has wrong format", addr)
@ -157,7 +157,7 @@ func TestNodeStatus(t *testing.T) {
func TestBlock(t *testing.T) {
waitForHeight(2)
tests.WaitForHeight(2, port)
var resultBlock ctypes.ResultBlock
@ -224,7 +224,7 @@ func TestCoinSend(t *testing.T) {
// create TX
receiveAddr, resultTx := doSend(t, port, seed)
waitForHeight(resultTx.Height + 1)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
@ -253,7 +253,7 @@ func TestIBCTransfer(t *testing.T) {
// create TX
resultTx := doIBCTransfer(t, port, seed)
waitForHeight(resultTx.Height + 1)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
@ -286,7 +286,7 @@ func TestTxs(t *testing.T) {
// create TX
_, resultTx := doSend(t, port, seed)
waitForHeight(resultTx.Height + 1)
tests.WaitForHeight(resultTx.Height+1, port)
// check if tx is findable
res, body := request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
@ -311,7 +311,11 @@ func TestTxs(t *testing.T) {
// strt TM and the LCD in process, listening on their respective sockets
func startTMAndLCD() (*nm.Node, net.Listener, error) {
viper.Set(cli.HomeFlag, os.TempDir())
dir, err := ioutil.TempDir("", "lcd_test")
if err != nil {
return nil, nil, err
}
viper.Set(cli.HomeFlag, dir)
kb, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
if err != nil {
return nil, nil, err
@ -376,7 +380,7 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
return nil, nil, err
}
waitForStart()
tests.WaitForStart(port)
return node, lcd, nil
}
@ -403,7 +407,7 @@ func startTM(cfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, p
}
// wait for rpc
waitForRPC()
tests.WaitForRPC(GetConfig().RPC.ListenAddress)
logger.Info("Tendermint running!")
return n, err
@ -486,71 +490,3 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
return resultTx
}
func waitForHeight(height int64) {
for {
var resultBlock ctypes.ResultBlock
url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest")
res, err := http.Get(url)
if err != nil {
panic(err)
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
res.Body.Close()
err = cdc.UnmarshalJSON([]byte(body), &resultBlock)
if err != nil {
fmt.Println("RES", res)
fmt.Println("BODY", string(body))
panic(err)
}
if resultBlock.Block.Height >= height {
return
}
time.Sleep(time.Millisecond * 100)
}
}
// wait for 2 blocks
func waitForStart() {
waitHeight := int64(2)
for {
time.Sleep(time.Second)
url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest")
res, err := http.Get(url)
if err != nil {
panic(err)
}
// waiting for server to start ...
if res.StatusCode != http.StatusOK {
res.Body.Close()
continue
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
res.Body.Close()
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
}
}
}

View File

@ -13,14 +13,15 @@ import (
cmn "github.com/tendermint/tmlibs/common"
client "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
keys "github.com/cosmos/cosmos-sdk/client/keys"
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
tx "github.com/cosmos/cosmos-sdk/client/tx"
version "github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/wire"
auth "github.com/cosmos/cosmos-sdk/x/auth/rest"
bank "github.com/cosmos/cosmos-sdk/x/bank/rest"
ibc "github.com/cosmos/cosmos-sdk/x/ibc/rest"
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"
)
const (
@ -66,19 +67,21 @@ func startRESTServerFn(cdc *wire.Codec) func(cmd *cobra.Command, args []string)
func createHandler(cdc *wire.Codec) http.Handler {
r := mux.NewRouter()
r.HandleFunc("/version", version.VersionRequestHandler).Methods("GET")
r.HandleFunc("/version", version.RequestHandler).Methods("GET")
kb, err := keys.GetKeyBase() //XXX
if err != nil {
panic(err)
}
ctx := context.NewCoreContextFromViper()
// TODO make more functional? aka r = keys.RegisterRoutes(r)
keys.RegisterRoutes(r)
rpc.RegisterRoutes(r)
tx.RegisterRoutes(r, cdc)
auth.RegisterRoutes(r, cdc, "main")
bank.RegisterRoutes(r, cdc, kb)
ibc.RegisterRoutes(r, cdc, kb)
rpc.RegisterRoutes(ctx, r)
tx.RegisterRoutes(ctx, r, cdc)
auth.RegisterRoutes(ctx, r, cdc, "acc")
bank.RegisterRoutes(ctx, r, cdc, kb)
ibc.RegisterRoutes(ctx, r, cdc, kb)
return r
}

View File

@ -20,6 +20,7 @@ func blockCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "block [height]",
Short: "Get verified data for a the block at given height",
Args: cobra.MaximumNArgs(1),
RunE: printBlock,
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
@ -29,9 +30,8 @@ func blockCommand() *cobra.Command {
return cmd
}
func getBlock(height *int64) ([]byte, error) {
func getBlock(ctx context.CoreContext, height *int64) ([]byte, error) {
// get the node
ctx := context.NewCoreContextFromViper()
node, err := ctx.GetNode()
if err != nil {
return nil, err
@ -55,8 +55,9 @@ func getBlock(height *int64) ([]byte, error) {
return output, nil
}
func GetChainHeight() (int64, error) {
node, err := context.NewCoreContextFromViper().GetNode()
// get the current blockchain height
func GetChainHeight(ctx context.CoreContext) (int64, error) {
node, err := ctx.GetNode()
if err != nil {
return -1, err
}
@ -84,7 +85,7 @@ func printBlock(cmd *cobra.Command, args []string) error {
}
}
output, err := getBlock(height)
output, err := getBlock(context.NewCoreContextFromViper(), height)
if err != nil {
return err
}
@ -94,41 +95,47 @@ func printBlock(cmd *cobra.Command, args []string) error {
// REST
func BlockRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
height, err := strconv.ParseInt(vars["height"], 10, 64)
if err != nil {
w.WriteHeader(400)
w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/block/{height}'."))
return
// REST handler to get a block
func BlockRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
height, err := strconv.ParseInt(vars["height"], 10, 64)
if err != nil {
w.WriteHeader(400)
w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/block/{height}'."))
return
}
chainHeight, err := GetChainHeight(ctx)
if height > chainHeight {
w.WriteHeader(404)
w.Write([]byte("ERROR: Requested block height is bigger then the chain length."))
return
}
output, err := getBlock(ctx, &height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
chainHeight, err := GetChainHeight()
if height > chainHeight {
w.WriteHeader(404)
w.Write([]byte("ERROR: Requested block height is bigger then the chain length."))
return
}
output, err := getBlock(&height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
func LatestBlockRequestHandler(w http.ResponseWriter, r *http.Request) {
height, err := GetChainHeight()
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
// REST handler to get the latest block
func LatestBlockRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
height, err := GetChainHeight(ctx)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
output, err := getBlock(ctx, &height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
output, err := getBlock(&height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}

View File

@ -6,6 +6,7 @@ import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
)
const (
@ -44,11 +45,12 @@ func initClientCommand() *cobra.Command {
return cmd
}
func RegisterRoutes(r *mux.Router) {
r.HandleFunc("/node_info", NodeInfoRequestHandler).Methods("GET")
r.HandleFunc("/syncing", NodeSyncingRequestHandler).Methods("GET")
r.HandleFunc("/blocks/latest", LatestBlockRequestHandler).Methods("GET")
r.HandleFunc("/blocks/{height}", BlockRequestHandler).Methods("GET")
r.HandleFunc("/validatorsets/latest", LatestValidatorsetRequestHandler).Methods("GET")
r.HandleFunc("/validatorsets/{height}", ValidatorsetRequestHandler).Methods("GET")
// Register REST endpoints
func RegisterRoutes(ctx context.CoreContext, r *mux.Router) {
r.HandleFunc("/node_info", NodeInfoRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/syncing", NodeSyncingRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/blocks/latest", LatestBlockRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/blocks/{height}", BlockRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/validatorsets/latest", LatestValidatorSetRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/validatorsets/{height}", ValidatorSetRequestHandlerFn(ctx)).Methods("GET")
}

View File

@ -22,9 +22,9 @@ func statusCommand() *cobra.Command {
return cmd
}
func getNodeStatus() (*ctypes.ResultStatus, error) {
func getNodeStatus(ctx context.CoreContext) (*ctypes.ResultStatus, error) {
// get the node
node, err := context.NewCoreContextFromViper().GetNode()
node, err := ctx.GetNode()
if err != nil {
return &ctypes.ResultStatus{}, err
}
@ -34,7 +34,7 @@ func getNodeStatus() (*ctypes.ResultStatus, error) {
// CMD
func printNodeStatus(cmd *cobra.Command, args []string) error {
status, err := getNodeStatus()
status, err := getNodeStatus(context.NewCoreContextFromViper())
if err != nil {
return err
}
@ -51,37 +51,43 @@ func printNodeStatus(cmd *cobra.Command, args []string) error {
// REST
func NodeInfoRequestHandler(w http.ResponseWriter, r *http.Request) {
status, err := getNodeStatus()
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
// REST handler for node info
func NodeInfoRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
status, err := getNodeStatus(ctx)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
nodeInfo := status.NodeInfo
output, err := cdc.MarshalJSON(nodeInfo)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
nodeInfo := status.NodeInfo
output, err := cdc.MarshalJSON(nodeInfo)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
w.Write(output)
}
func NodeSyncingRequestHandler(w http.ResponseWriter, r *http.Request) {
status, err := getNodeStatus()
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
// REST handler for node syncing
func NodeSyncingRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
status, err := getNodeStatus(ctx)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
syncing := status.SyncInfo.Syncing
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
syncing := status.SyncInfo.Syncing
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write([]byte(strconv.FormatBool(syncing)))
}
w.Write([]byte(strconv.FormatBool(syncing)))
}

View File

@ -12,10 +12,13 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
)
// TODO these next two functions feel kinda hacky based on their placement
func validatorCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "validatorset <height>",
Use: "validatorset [height]",
Short: "Get the full validator set at given height",
Args: cobra.MaximumNArgs(1),
RunE: printValidators,
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
@ -24,9 +27,9 @@ func validatorCommand() *cobra.Command {
return cmd
}
func GetValidators(height *int64) ([]byte, error) {
func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) {
// get the node
node, err := context.NewCoreContextFromViper().GetNode()
node, err := ctx.GetNode()
if err != nil {
return nil, err
}
@ -59,7 +62,7 @@ func printValidators(cmd *cobra.Command, args []string) error {
}
}
output, err := GetValidators(height)
output, err := getValidators(context.NewCoreContextFromViper(), height)
if err != nil {
return err
}
@ -70,41 +73,47 @@ func printValidators(cmd *cobra.Command, args []string) error {
// REST
func ValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
height, err := strconv.ParseInt(vars["height"], 10, 64)
if err != nil {
w.WriteHeader(400)
w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/validatorsets/{height}'."))
return
// Validator Set at a height REST handler
func ValidatorSetRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
height, err := strconv.ParseInt(vars["height"], 10, 64)
if err != nil {
w.WriteHeader(400)
w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/validatorsets/{height}'."))
return
}
chainHeight, err := GetChainHeight(ctx)
if height > chainHeight {
w.WriteHeader(404)
w.Write([]byte("ERROR: Requested block height is bigger then the chain length."))
return
}
output, err := getValidators(ctx, &height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
chainHeight, err := GetChainHeight()
if height > chainHeight {
w.WriteHeader(404)
w.Write([]byte("ERROR: Requested block height is bigger then the chain length."))
return
}
output, err := GetValidators(&height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
func LatestValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
height, err := GetChainHeight()
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
// Latest Validator Set REST handler
func LatestValidatorSetRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
height, err := GetChainHeight(ctx)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
output, err := getValidators(ctx, &height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
output, err := GetValidators(&height)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}

View File

@ -7,27 +7,31 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
)
// Tx Broadcast Body
type BroadcastTxBody struct {
TxBytes string `json="tx"`
}
func BroadcastTxRequestHandler(w http.ResponseWriter, r *http.Request) {
var m BroadcastTxBody
// BroadcastTx REST Handler
func BroadcastTxRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m BroadcastTxBody
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&m)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&m)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
res, err := ctx.BroadcastTx([]byte(m.TxBytes))
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write([]byte(string(res.Height)))
}
res, err := context.NewCoreContextFromViper().BroadcastTx([]byte(m.TxBytes))
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
return
}
w.Write([]byte(string(res.Height)))
}

View File

@ -8,7 +8,6 @@ import (
"strconv"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
abci "github.com/tendermint/abci/types"
@ -21,26 +20,42 @@ import (
)
// Get the default command for a tx query
func QueryTxCmd(cmdr commander) *cobra.Command {
func QueryTxCmd(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "tx [hash]",
Short: "Matches this txhash over all committed blocks",
RunE: cmdr.queryAndPrintTx,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
// find the key to look up the account
hashHexStr := args[0]
trustNode := viper.GetBool(client.FlagTrustNode)
output, err := queryTx(cdc, context.NewCoreContextFromViper(), hashHexStr, trustNode)
if err != nil {
return err
}
fmt.Println(string(output))
return nil
},
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
// TODO: change this to false when we can
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
return cmd
}
func (c commander) queryTx(hashHexStr string, trustNode bool) ([]byte, error) {
func queryTx(cdc *wire.Codec, ctx context.CoreContext, hashHexStr string, trustNode bool) ([]byte, error) {
hash, err := hex.DecodeString(hashHexStr)
if err != nil {
return nil, err
}
// get the node
node, err := context.NewCoreContextFromViper().GetNode()
node, err := ctx.GetNode()
if err != nil {
return nil, err
}
@ -49,7 +64,7 @@ func (c commander) queryTx(hashHexStr string, trustNode bool) ([]byte, error) {
if err != nil {
return nil, err
}
info, err := formatTxResult(c.cdc, res)
info, err := formatTxResult(cdc, res)
if err != nil {
return nil, err
}
@ -88,31 +103,10 @@ func parseTx(cdc *wire.Codec, txBytes []byte) (sdk.Tx, error) {
return tx, nil
}
// CMD
// command to query for a transaction
func (c commander) queryAndPrintTx(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide a tx hash")
}
// find the key to look up the account
hashHexStr := args[0]
trustNode := viper.GetBool(client.FlagTrustNode)
output, err := c.queryTx(hashHexStr, trustNode)
if err != nil {
return err
}
fmt.Println(string(output))
return nil
}
// REST
func QueryTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Request) {
c := commander{cdc}
// transaction query REST handler
func QueryTxRequestHandlerFn(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
hashHexStr := vars["hash"]
@ -122,7 +116,7 @@ func QueryTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Requ
trustNode = true
}
output, err := c.queryTx(hashHexStr, trustNode)
output, err := queryTx(cdc, ctx, hashHexStr, trustNode)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))

View File

@ -4,26 +4,22 @@ import (
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/wire"
)
// type used to pass around the provided cdc
type commander struct {
cdc *wire.Codec
}
// AddCommands adds a number of tx-query related subcommands
func AddCommands(cmd *cobra.Command, cdc *wire.Codec) {
cmdr := commander{cdc}
cmd.AddCommand(
SearchTxCmd(cmdr),
QueryTxCmd(cmdr),
SearchTxCmd(cdc),
QueryTxCmd(cdc),
)
}
func RegisterRoutes(r *mux.Router, cdc *wire.Codec) {
// register REST routes
func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) {
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, ctx)).Methods("GET")
// r.HandleFunc("/txs", SearchTxRequestHandler(cdc)).Methods("GET")
r.HandleFunc("/txs/{hash}", QueryTxRequestHandler(cdc)).Methods("GET")
// r.HandleFunc("/txs/sign", SignTxRequstHandler).Methods("POST")
// r.HandleFunc("/txs/broadcast", BroadcastTxRequestHandler).Methods("POST")
}

View File

@ -22,13 +22,24 @@ const (
)
// default client command to search through tagged transactions
func SearchTxCmd(cmdr commander) *cobra.Command {
func SearchTxCmd(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "txs",
Short: "Search for all transactions that match the given tags",
RunE: cmdr.searchAndPrintTx,
RunE: func(cmd *cobra.Command, args []string) error {
tags := viper.GetStringSlice(flagTags)
output, err := searchTx(context.NewCoreContextFromViper(), cdc, tags)
if err != nil {
return err
}
fmt.Println(string(output))
return nil
},
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
// TODO: change this to false once proofs built in
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)")
@ -36,7 +47,7 @@ func SearchTxCmd(cmdr commander) *cobra.Command {
return cmd
}
func (c commander) searchTx(tags []string) ([]byte, error) {
func searchTx(ctx context.CoreContext, cdc *wire.Codec, tags []string) ([]byte, error) {
if len(tags) == 0 {
return nil, errors.New("Must declare at least one tag to search")
}
@ -44,7 +55,7 @@ func (c commander) searchTx(tags []string) ([]byte, error) {
query := strings.Join(tags, " AND ")
// get the node
node, err := context.NewCoreContextFromViper().GetNode()
node, err := ctx.GetNode()
if err != nil {
return nil, err
}
@ -55,12 +66,12 @@ func (c commander) searchTx(tags []string) ([]byte, error) {
return nil, err
}
info, err := formatTxResults(c.cdc, res)
info, err := formatTxResults(cdc, res)
if err != nil {
return nil, err
}
output, err := c.cdc.MarshalJSON(info)
output, err := cdc.MarshalJSON(info)
if err != nil {
return nil, err
}
@ -79,24 +90,11 @@ func formatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]txInfo, error)
return out, nil
}
// CMD
func (c commander) searchAndPrintTx(cmd *cobra.Command, args []string) error {
tags := viper.GetStringSlice(flagTags)
output, err := c.searchTx(tags)
if err != nil {
return err
}
fmt.Println(string(output))
return nil
}
/////////////////////////////////////////
// REST
func SearchTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Request) {
c := commander{cdc}
// Search Tx REST Handler
func SearchTxRequestHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
tag := r.FormValue("tag")
if tag == "" {
@ -106,7 +104,7 @@ func SearchTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Req
}
tags := []string{tag}
output, err := c.searchTx(tags)
output, err := searchTx(ctx, cdc, tags)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))

View File

@ -8,12 +8,15 @@ import (
keys "github.com/tendermint/go-crypto/keys"
)
// REST request body
// TODO does this need to be exposed?
type SignTxBody struct {
Name string `json="name"`
Password string `json="password"`
TxBytes string `json="tx"`
}
// sign transaction REST Handler
func SignTxRequstHandler(w http.ResponseWriter, r *http.Request) {
var kb keys.Keybase
var m SignTxBody

146
cmd/gaia/app/app.go Normal file
View File

@ -0,0 +1,146 @@
package app
import (
"encoding/json"
"os"
abci "github.com/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
bam "github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/stake"
)
const (
appName = "GaiaApp"
)
// default home directories for expected binaries
var (
DefaultCLIHome = os.ExpandEnv("$HOME/.gaiacli")
DefaultNodeHome = os.ExpandEnv("$HOME/.gaiad")
)
// Extended ABCI application
type GaiaApp struct {
*bam.BaseApp
cdc *wire.Codec
// keys to access the substores
keyMain *sdk.KVStoreKey
keyAccount *sdk.KVStoreKey
keyIBC *sdk.KVStoreKey
keyStake *sdk.KVStoreKey
// Manage getting and setting accounts
accountMapper sdk.AccountMapper
coinKeeper bank.Keeper
ibcMapper ibc.Mapper
stakeKeeper stake.Keeper
}
func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
cdc := MakeCodec()
// 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"),
}
// define the accountMapper
app.accountMapper = auth.NewAccountMapper(
app.cdc,
app.keyAccount, // target store
&auth.BaseAccount{}, // prototype
)
// add handlers
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))
// register message routes
app.Router().
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
AddRoute("stake", stake.NewHandler(app.stakeKeeper))
// initialize BaseApp
app.SetInitChainer(app.initChainer)
app.SetEndBlocker(stake.NewEndBlocker(app.stakeKeeper))
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, stake.FeeHandler))
err := app.LoadLatestVersion(app.keyMain)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
// custom tx codec
func MakeCodec() *wire.Codec {
var cdc = wire.NewCodec()
ibc.RegisterWire(cdc)
bank.RegisterWire(cdc)
stake.RegisterWire(cdc)
auth.RegisterWire(cdc)
sdk.RegisterWire(cdc)
wire.RegisterCrypto(cdc)
return cdc
}
// custom logic for gaia initialization
func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes
var genesisState GenesisState
err := app.cdc.UnmarshalJSON(stateJSON, &genesisState)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
}
// load the accounts
for _, gacc := range genesisState.Accounts {
acc := gacc.ToAccount()
app.accountMapper.SetAccount(ctx, acc)
}
// load the initial stake information
stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
return abci.ResponseInitChain{}
}
// export the state of gaia for a genesis f
func (app *GaiaApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
ctx := app.NewContext(true, abci.Header{})
// iterate to get the accounts
accounts := []GenesisAccount{}
appendAccount := func(acc sdk.Account) (stop bool) {
account := NewGenesisAccountI(acc)
accounts = append(accounts, account)
return false
}
app.accountMapper.IterateAccounts(ctx, appendAccount)
genState := GenesisState{
Accounts: accounts,
StakeData: stake.WriteGenesis(ctx, app.stakeKeeper),
}
return wire.MarshalJSONIndent(app.cdc, genState)
}

519
cmd/gaia/app/app_test.go Normal file
View File

@ -0,0 +1,519 @@
package app
import (
"encoding/json"
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
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"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
)
// Construct some global addrs and txs for tests.
var (
chainID = "" // TODO
accName = "foobart"
priv1 = crypto.GenPrivKeyEd25519()
addr1 = priv1.PubKey().Address()
priv2 = crypto.GenPrivKeyEd25519()
addr2 = priv2.PubKey().Address()
addr3 = crypto.GenPrivKeyEd25519().PubKey().Address()
priv4 = crypto.GenPrivKeyEd25519()
addr4 = priv4.PubKey().Address()
coins = sdk.Coins{{"foocoin", 10}}
halfCoins = sdk.Coins{{"foocoin", 5}}
manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}}
fee = sdk.StdFee{
sdk.Coins{{"foocoin", 0}},
0,
}
sendMsg1 = bank.MsgSend{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
}
sendMsg2 = bank.MsgSend{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{
bank.NewOutput(addr2, halfCoins),
bank.NewOutput(addr3, halfCoins),
},
}
sendMsg3 = bank.MsgSend{
Inputs: []bank.Input{
bank.NewInput(addr1, coins),
bank.NewInput(addr4, coins),
},
Outputs: []bank.Output{
bank.NewOutput(addr2, coins),
bank.NewOutput(addr3, coins),
},
}
sendMsg4 = bank.MsgSend{
Inputs: []bank.Input{
bank.NewInput(addr2, coins),
},
Outputs: []bank.Output{
bank.NewOutput(addr1, coins),
},
}
sendMsg5 = bank.MsgSend{
Inputs: []bank.Input{
bank.NewInput(addr1, manyCoins),
},
Outputs: []bank.Output{
bank.NewOutput(addr2, manyCoins),
},
}
)
func loggerAndDB() (log.Logger, dbm.DB) {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
db := dbm.NewMemDB()
return logger, db
}
func newGaiaApp() *GaiaApp {
logger, db := loggerAndDB()
return NewGaiaApp(logger, db)
}
func setGenesis(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.GetDefaultGenesisState(),
}
stateBytes, err := wire.MarshalJSONIndent(gapp.cdc, genesisState)
if err != nil {
return err
}
// Initialize the chain
vals := []abci.Validator{}
gapp.InitChain(abci.RequestInitChain{vals, stateBytes})
gapp.Commit()
return nil
}
//_______________________________________________________________________
func TestMsgs(t *testing.T) {
gapp := newGaiaApp()
require.Nil(t, setGenesis(gapp))
msgs := []struct {
msg sdk.Msg
}{
{sendMsg1},
}
for i, m := range msgs {
// Run a CheckDeliver
SignCheckDeliver(t, gapp, m.msg, []int64{int64(i)}, false, priv1)
}
}
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.GetDefaultGenesisState(),
}
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)
// Construct some genesis bytes to reflect GaiaAccount
pk := crypto.GenPrivKeyEd25519().PubKey()
addr := pk.Address()
coins, err := sdk.ParseCoins("77foocoin,99barcoin")
require.Nil(t, err)
baseAcc := &auth.BaseAccount{
Address: addr,
Coins: coins,
}
err = setGenesis(gapp, baseAcc)
assert.Nil(t, err)
// A checkTx context
ctx := gapp.BaseApp.NewContext(true, abci.Header{})
res1 := gapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, baseAcc, res1)
// reload app and ensure the account is still there
gapp = NewGaiaApp(logger, dbs)
ctx = gapp.BaseApp.NewContext(true, abci.Header{})
res1 = gapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, baseAcc, res1)
}
func TestMsgSendWithAccounts(t *testing.T) {
gapp := newGaiaApp()
// Construct some genesis bytes to reflect GaiaAccount
// Give 77 foocoin to the first key
coins, err := sdk.ParseCoins("77foocoin")
require.Nil(t, err)
baseAcc := &auth.BaseAccount{
Address: addr1,
Coins: coins,
}
// Construct genesis state
err = setGenesis(gapp, baseAcc)
require.Nil(t, err)
// A checkTx context (true)
ctxCheck := gapp.BaseApp.NewContext(true, abci.Header{})
res1 := gapp.accountMapper.GetAccount(ctxCheck, addr1)
assert.Equal(t, baseAcc, res1.(*auth.BaseAccount))
// Run a CheckDeliver
SignCheckDeliver(t, gapp, sendMsg1, []int64{0}, true, priv1)
// Check balances
CheckBalance(t, gapp, addr1, "67foocoin")
CheckBalance(t, gapp, addr2, "10foocoin")
// Delivering again should cause replay error
SignCheckDeliver(t, gapp, sendMsg1, []int64{0}, false, priv1)
// bumping the txnonce number without resigning should be an auth error
tx := genTx(sendMsg1, []int64{0}, priv1)
tx.Signatures[0].Sequence = 1
res := gapp.Deliver(tx)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
// resigning the tx with the bumped sequence should work
SignCheckDeliver(t, gapp, sendMsg1, []int64{1}, true, priv1)
}
func TestMsgSendMultipleOut(t *testing.T) {
gapp := newGaiaApp()
genCoins, err := sdk.ParseCoins("42foocoin")
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)
// Simulate a Block
SignCheckDeliver(t, gapp, sendMsg2, []int64{0}, true, priv1)
// Check balances
CheckBalance(t, gapp, addr1, "32foocoin")
CheckBalance(t, gapp, addr2, "47foocoin")
CheckBalance(t, gapp, addr3, "5foocoin")
}
func TestSengMsgMultipleInOut(t *testing.T) {
gapp := newGaiaApp()
genCoins, err := sdk.ParseCoins("42foocoin")
require.Nil(t, err)
acc1 := &auth.BaseAccount{
Address: addr1,
Coins: genCoins,
}
acc2 := &auth.BaseAccount{
Address: addr2,
Coins: genCoins,
}
acc4 := &auth.BaseAccount{
Address: addr4,
Coins: genCoins,
}
err = setGenesis(gapp, acc1, acc2, acc4)
assert.Nil(t, err)
// CheckDeliver
SignCheckDeliver(t, gapp, sendMsg3, []int64{0, 0}, true, priv1, priv4)
// Check balances
CheckBalance(t, gapp, addr1, "32foocoin")
CheckBalance(t, gapp, addr4, "32foocoin")
CheckBalance(t, gapp, addr2, "52foocoin")
CheckBalance(t, gapp, addr3, "10foocoin")
}
func TestMsgSendDependent(t *testing.T) {
gapp := newGaiaApp()
genCoins, err := sdk.ParseCoins("42foocoin")
require.Nil(t, err)
acc1 := &auth.BaseAccount{
Address: addr1,
Coins: genCoins,
}
err = setGenesis(gapp, acc1)
require.Nil(t, err)
// CheckDeliver
SignCheckDeliver(t, gapp, sendMsg1, []int64{0}, true, priv1)
// Check balances
CheckBalance(t, gapp, addr1, "32foocoin")
CheckBalance(t, gapp, addr2, "10foocoin")
// Simulate a Block
SignCheckDeliver(t, gapp, sendMsg4, []int64{0}, true, priv2)
// Check balances
CheckBalance(t, gapp, addr1, "42foocoin")
}
func TestIBCMsgs(t *testing.T) {
gapp := newGaiaApp()
sourceChain := "source-chain"
destChain := "dest-chain"
baseAcc := &auth.BaseAccount{
Address: addr1,
Coins: coins,
}
err := setGenesis(gapp, baseAcc)
require.Nil(t, err)
// A checkTx context (true)
ctxCheck := gapp.BaseApp.NewContext(true, abci.Header{})
res1 := gapp.accountMapper.GetAccount(ctxCheck, addr1)
assert.Equal(t, baseAcc, res1)
packet := ibc.IBCPacket{
SrcAddr: addr1,
DestAddr: addr1,
Coins: coins,
SrcChain: sourceChain,
DestChain: destChain,
}
transferMsg := ibc.IBCTransferMsg{
IBCPacket: packet,
}
receiveMsg := ibc.IBCReceiveMsg{
IBCPacket: packet,
Relayer: addr1,
Sequence: 0,
}
SignCheckDeliver(t, gapp, transferMsg, []int64{0}, true, priv1)
CheckBalance(t, gapp, addr1, "")
SignCheckDeliver(t, gapp, transferMsg, []int64{1}, false, priv1)
SignCheckDeliver(t, gapp, receiveMsg, []int64{2}, true, priv1)
CheckBalance(t, gapp, addr1, "10foocoin")
SignCheckDeliver(t, gapp, receiveMsg, []int64{3}, false, priv1)
}
func TestStakeMsgs(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)
// A checkTx context (true)
ctxCheck := gapp.BaseApp.NewContext(true, abci.Header{})
res1 := gapp.accountMapper.GetAccount(ctxCheck, addr1)
res2 := gapp.accountMapper.GetAccount(ctxCheck, addr2)
require.Equal(t, acc1, res1)
require.Equal(t, acc2, res2)
// Declare Candidacy
description := stake.NewDescription("foo_moniker", "", "", "")
declareCandidacyMsg := stake.NewMsgDeclareCandidacy(
addr1, priv1.PubKey(), bondCoin, description,
)
SignCheckDeliver(t, gapp, declareCandidacyMsg, []int64{0}, true, priv1)
ctxDeliver := gapp.BaseApp.NewContext(false, abci.Header{})
res1 = gapp.accountMapper.GetAccount(ctxDeliver, addr1)
require.Equal(t, genCoins.Minus(sdk.Coins{bondCoin}), res1.GetCoins())
candidate, found := gapp.stakeKeeper.GetCandidate(ctxDeliver, addr1)
require.True(t, found)
require.Equal(t, candidate.Address, addr1)
// Edit Candidacy
description = stake.NewDescription("bar_moniker", "", "", "")
editCandidacyMsg := stake.NewMsgEditCandidacy(
addr1, description,
)
SignDeliver(t, gapp, editCandidacyMsg, []int64{1}, true, priv1)
candidate, found = gapp.stakeKeeper.GetCandidate(ctxDeliver, addr1)
require.True(t, found)
require.Equal(t, candidate.Description, description)
// Delegate
delegateMsg := stake.NewMsgDelegate(
addr2, addr1, bondCoin,
)
SignDeliver(t, gapp, delegateMsg, []int64{0}, true, priv2)
ctxDeliver = gapp.BaseApp.NewContext(false, abci.Header{})
res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2)
require.Equal(t, genCoins.Minus(sdk.Coins{bondCoin}), res2.GetCoins())
bond, found := gapp.stakeKeeper.GetDelegatorBond(ctxDeliver, addr2, addr1)
require.True(t, found)
require.Equal(t, bond.DelegatorAddr, addr2)
// Unbond
unbondMsg := stake.NewMsgUnbond(
addr2, addr1, "MAX",
)
SignDeliver(t, gapp, unbondMsg, []int64{1}, true, priv2)
ctxDeliver = gapp.BaseApp.NewContext(false, abci.Header{})
res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2)
require.Equal(t, genCoins, res2.GetCoins())
_, found = gapp.stakeKeeper.GetDelegatorBond(ctxDeliver, addr2, addr1)
require.False(t, found)
}
//____________________________________________________________________________________
func CheckBalance(t *testing.T, gapp *GaiaApp, addr sdk.Address, balExpected string) {
ctxDeliver := gapp.BaseApp.NewContext(false, abci.Header{})
res2 := gapp.accountMapper.GetAccount(ctxDeliver, addr)
assert.Equal(t, balExpected, fmt.Sprintf("%v", res2.GetCoins()))
}
func genTx(msg sdk.Msg, seq []int64, priv ...crypto.PrivKeyEd25519) sdk.StdTx {
sigs := make([]sdk.StdSignature, len(priv))
for i, p := range priv {
sigs[i] = sdk.StdSignature{
PubKey: p.PubKey(),
Signature: p.Sign(sdk.StdSignBytes(chainID, seq, fee, msg)),
Sequence: seq[i],
}
}
return sdk.NewStdTx(msg, fee, sigs)
}
func SignCheckDeliver(t *testing.T, gapp *GaiaApp, msg sdk.Msg, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) {
// Sign the tx
tx := genTx(msg, seq, priv...)
// Run a Check
res := gapp.Check(tx)
if expPass {
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
}
// Simulate a Block
gapp.BeginBlock(abci.RequestBeginBlock{})
res = gapp.Deliver(tx)
if expPass {
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
}
gapp.EndBlock(abci.RequestEndBlock{})
// XXX fix code or add explaination as to why using commit breaks a bunch of these tests
//gapp.Commit()
}
// XXX the only reason we are using Sign Deliver here is because the tests
// break on check tx the second time you use SignCheckDeliver in a test because
// the checktx state has not been updated likely because commit is not being
// called!
func SignDeliver(t *testing.T, gapp *GaiaApp, msg sdk.Msg, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) {
// Sign the tx
tx := genTx(msg, seq, priv...)
// Simulate a Block
gapp.BeginBlock(abci.RequestBeginBlock{})
res := gapp.Deliver(tx)
if expPass {
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
}
gapp.EndBlock(abci.RequestEndBlock{})
}

180
cmd/gaia/app/genesis.go Normal file
View File

@ -0,0 +1,180 @@
package app
import (
"encoding/json"
"errors"
"github.com/spf13/pflag"
"github.com/spf13/viper"
crypto "github.com/tendermint/go-crypto"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/server"
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"
)
// State to Unmarshal
type GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
StakeData stake.GenesisState `json:"stake"`
}
// GenesisAccount doesn't need pubkey or sequence
type GenesisAccount struct {
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
}
func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount {
return GenesisAccount{
Address: acc.Address,
Coins: acc.Coins,
}
}
func NewGenesisAccountI(acc sdk.Account) GenesisAccount {
return GenesisAccount{
Address: acc.GetAddress(),
Coins: acc.GetCoins(),
}
}
// convert GenesisAccount to auth.BaseAccount
func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) {
return &auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins.Sort(),
}
}
var (
flagName = "name"
flagClientHome = "home-client"
flagOWK = "owk"
// bonded tokens given to genesis validators/accounts
freeFermionVal = int64(100)
freeFermionsAcc = int64(50)
)
// get app init parameters for server init command
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(flagClientHome, DefaultCLIHome, "home directory for the client, used for key generation")
fsAppGenTx.Bool(flagOWK, false, "overwrite the for the accounts created")
return server.AppInit{
FlagsAppGenState: fsAppGenState,
FlagsAppGenTx: fsAppGenTx,
AppGenTx: GaiaAppGenTx,
AppGenState: GaiaAppGenState,
}
}
// simple genesis tx
type GaiaGenTx struct {
Name string `json:"name"`
Address sdk.Address `json:"address"`
PubKey crypto.PubKey `json:"pub_key"`
}
// Generate a gaia genesis transaction
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)
addr, secret, err = server.GenerateSaveCoinKey(clientRoot, name, "1234567890", overwrite)
if err != nil {
return
}
var bz []byte
gaiaGenTx := GaiaGenTx{
Name: name,
Address: addr,
PubKey: pk,
}
bz, err = wire.MarshalJSONIndent(cdc, gaiaGenTx)
if err != nil {
return
}
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,
}
return
}
// 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) {
if len(appGenTxs) == 0 {
err = errors.New("must provide at least genesis transaction")
return
}
// start with the default staking genesis state
stakeData := stake.GetDefaultGenesisState()
// get genesis flag account information
genaccs := make([]GenesisAccount, len(appGenTxs))
for i, appGenTx := range appGenTxs {
var genTx GaiaGenTx
err = cdc.UnmarshalJSON(appGenTx, &genTx)
if err != nil {
return
}
// create the genesis account, give'm few steaks and a buncha token with there name
accAuth := auth.NewBaseAccountWithAddress(genTx.Address)
accAuth.Coins = sdk.Coins{
{genTx.Name + "Token", 1000},
{"steak", freeFermionsAcc},
}
acc := NewGenesisAccount(&accAuth)
genaccs[i] = acc
stakeData.Pool.TotalSupply += freeFermionsAcc // increase the supply
// add the validator
if len(genTx.Name) > 0 {
desc := stake.NewDescription(genTx.Name, "", "", "")
candidate := stake.NewCandidate(genTx.Address, genTx.PubKey, desc)
candidate.Assets = sdk.NewRat(freeFermionVal)
stakeData.Candidates = append(stakeData.Candidates, candidate)
// pool logic
stakeData.Pool.TotalSupply += freeFermionVal
stakeData.Pool.BondedPool += freeFermionVal
stakeData.Pool.BondedShares = sdk.NewRat(stakeData.Pool.BondedPool)
}
}
// create the final app state
genesisState := GenesisState{
Accounts: genaccs,
StakeData: stakeData,
}
appState, err = wire.MarshalJSONIndent(cdc, genesisState)
return
}

View File

@ -0,0 +1,36 @@
package app
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/stretchr/testify/assert"
crypto "github.com/tendermint/go-crypto"
)
func TestToAccount(t *testing.T) {
priv := crypto.GenPrivKeyEd25519()
addr := sdk.Address(priv.PubKey().Address())
authAcc := auth.NewBaseAccountWithAddress(addr)
genAcc := NewGenesisAccount(&authAcc)
assert.Equal(t, authAcc, *genAcc.ToAccount())
}
func TestGaiaAppGenTx(t *testing.T) {
cdc := MakeCodec()
_ = cdc
//TODO test that key overwrite flags work / no overwrites if set off
//TODO test validator created has provided pubkey
//TODO test the account created has the correct pubkey
}
func TestGaiaAppGenState(t *testing.T) {
cdc := MakeCodec()
_ = cdc
// TODO test must provide at least genesis transaction
// TODO test with both one and two genesis transactions:
// TODO correct: genesis account created, canididates created, pool token variance
}

View File

@ -0,0 +1,190 @@
package clitest
import (
"encoding/json"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/tests"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/stake"
)
func TestGaiaCLISend(t *testing.T) {
tests.ExecuteT(t, "gaiad unsafe_reset_all")
pass := "1234567890"
executeWrite(t, "gaiacli keys delete foo", pass)
executeWrite(t, "gaiacli keys delete bar", pass)
chainID := executeInit(t, "gaiad init -o --name=foo")
executeWrite(t, "gaiacli keys add bar", pass)
// get a free port, also setup some common flags
servAddr := server.FreeTCPAddr(t)
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()
fooAddr, _ := executeGetAddrPK(t, "gaiacli keys show foo --output=json")
barAddr, _ := executeGetAddrPK(t, "gaiacli keys show bar --output=json")
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, 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
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags))
assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak"))
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, 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
barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags))
assert.Equal(t, int64(20), barAcc.GetCoins().AmountOf("steak"))
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, flags))
assert.Equal(t, int64(30), fooAcc.GetCoins().AmountOf("steak"))
}
func TestGaiaCLIDeclareCandidacy(t *testing.T) {
tests.ExecuteT(t, "gaiad unsafe_reset_all")
pass := "1234567890"
executeWrite(t, "gaiacli keys delete foo", pass)
executeWrite(t, "gaiacli keys delete bar", pass)
chainID := executeInit(t, "gaiad init -o --name=foo")
executeWrite(t, "gaiacli keys add bar", pass)
// get a free port, also setup some common flags
servAddr := server.FreeTCPAddr(t)
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()
fooAddr, _ := executeGetAddrPK(t, "gaiacli keys show foo --output=json")
barAddr, barPubKey := executeGetAddrPK(t, "gaiacli keys show bar --output=json")
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
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, flags))
assert.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak"))
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags))
assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak"))
// declare candidacy
declStr := fmt.Sprintf("gaiacli declare-candidacy %v", flags)
declStr += fmt.Sprintf(" --name=%v", "bar")
declStr += fmt.Sprintf(" --address-candidate=%v", barAddr)
declStr += fmt.Sprintf(" --pubkey=%v", barPubKey)
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.Address.String(), barAddr)
assert.Equal(t, int64(3), candidate.Assets.Evaluate())
// 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.Assets.Evaluate())
}
func executeWrite(t *testing.T, cmdStr string, writes ...string) {
cmd, wc, _ := 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()
}
func executeWritePrint(t *testing.T, cmdStr string, writes ...string) {
cmd, wc, rc := tests.GoExecuteT(t, cmdStr)
for _, write := range writes {
_, err := wc.Write([]byte(write + "\n"))
require.NoError(t, err)
}
fmt.Printf("debug waiting cmdStr: %v\n", cmdStr)
cmd.Wait()
bz := make([]byte, 100000)
rc.Read(bz)
fmt.Printf("debug read: %v\n", string(bz))
}
func executeInit(t *testing.T, cmdStr string) (chainID string) {
out := tests.ExecuteT(t, cmdStr)
var initRes map[string]json.RawMessage
err := json.Unmarshal([]byte(out), &initRes)
require.NoError(t, err)
err = json.Unmarshal(initRes["chain_id"], &chainID)
require.NoError(t, err)
return
}
func executeGetAddrPK(t *testing.T, cmdStr string) (addr, pubKey string) {
out := tests.ExecuteT(t, cmdStr)
var ko keys.KeyOutput
keys.UnmarshalJSON([]byte(out), &ko)
return ko.Address, ko.PubKey
}
func executeGetAccount(t *testing.T, cmdStr string) auth.BaseAccount {
out := tests.ExecuteT(t, cmdStr)
var initRes map[string]json.RawMessage
err := json.Unmarshal([]byte(out), &initRes)
require.NoError(t, err, "out %v, err %v", out, err)
value := initRes["value"]
var acc auth.BaseAccount
cdc := wire.NewCodec()
wire.RegisterCrypto(cdc)
err = cdc.UnmarshalJSON(value, &acc)
require.NoError(t, err, "value %v, err %v", string(value), err)
return acc
}
func executeGetCandidate(t *testing.T, cmdStr string) stake.Candidate {
out := tests.ExecuteT(t, cmdStr)
var candidate stake.Candidate
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &candidate)
require.NoError(t, err, "out %v, err %v", out, err)
return candidate
}

View File

@ -1,8 +1,6 @@
package main
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tmlibs/cli"
@ -12,19 +10,15 @@ import (
"github.com/cosmos/cosmos-sdk/client/lcd"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands"
simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands"
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"
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
)
// TODO: distinguish from basecli
// rootCmd is the entry point for this binary
var (
rootCmd = &cobra.Command{
@ -34,10 +28,7 @@ var (
)
func main() {
// disable sorting
cobra.EnableCommandSorting = false
// get the codec
cdc := app.MakeCodec()
// TODO: setup keybase, viper object, etc. to be passed into
@ -53,24 +44,21 @@ func main() {
// add query/post commands (custom to binary)
rootCmd.AddCommand(
client.GetCommands(
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)),
stakecmd.GetCmdQueryCandidate("stake", cdc),
//stakecmd.GetCmdQueryCandidates("stake", cdc),
stakecmd.GetCmdQueryDelegatorBond("stake", cdc),
//stakecmd.GetCmdQueryDelegatorBonds("stake", cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
bankcmd.SendTxCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCTransferCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCRelayCmd(cdc),
simplestakingcmd.BondTxCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
simplestakingcmd.UnbondTxCmd(cdc),
stakecmd.GetCmdDeclareCandidacy(cdc),
stakecmd.GetCmdEditCandidacy(cdc),
stakecmd.GetCmdDelegate(cdc),
stakecmd.GetCmdUnbond(cdc),
)...)
// add proxy, version and key info
@ -83,6 +71,6 @@ func main() {
)
// prepare and add flags
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.gaiacli"))
executor := cli.PrepareMainCmd(rootCmd, "GA", app.DefaultCLIHome)
executor.Execute()
}

View File

@ -0,0 +1,42 @@
package main
import (
"encoding/json"
"github.com/spf13/cobra"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/server"
)
func main() {
cdc := app.MakeCodec()
ctx := server.NewDefaultContext()
rootCmd := &cobra.Command{
Use: "gaiad",
Short: "Gaia Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
server.AddCommands(ctx, cdc, rootCmd, app.GaiaAppInit(),
server.ConstructAppCreator(newApp, "gaia"),
server.ConstructAppExporter(exportAppState, "gaia"))
// prepare and add flags
executor := cli.PrepareBaseCmd(rootCmd, "GA", app.DefaultNodeHome)
executor.Execute()
}
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) {
gapp := app.NewGaiaApp(logger, db)
return gapp.ExportAppStateJSON()
}

View File

@ -1,45 +0,0 @@
package main
import (
"os"
"path/filepath"
"github.com/spf13/cobra"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
"github.com/cosmos/cosmos-sdk/server"
)
// rootCmd is the entry point for this binary
var (
context = server.NewDefaultContext()
rootCmd = &cobra.Command{
Use: "gaiad",
Short: "Gaia Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(context),
}
)
// TODO: distinguish from basecoin
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
dataDir := filepath.Join(rootDir, "data")
db, err := dbm.NewGoLevelDB("gaia", dataDir)
if err != nil {
return nil, err
}
bapp := app.NewBasecoinApp(logger, db)
return bapp, nil
}
func main() {
server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context)
// prepare and add flags
executor := cli.PrepareBaseCmd(rootCmd, "GA", os.ExpandEnv("$HOME/.gaiad"))
executor.Execute()
}

View File

@ -47,7 +47,7 @@ master_doc = 'index'
# General information about the project.
project = u'Cosmos-SDK'
copyright = u'2017, The Authors'
copyright = u'2018, The Authors'
author = u'The Authors'
# The version info for the project you're documenting, acts as replacement for
@ -69,7 +69,7 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'old']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

View File

@ -29,9 +29,6 @@ type Msg interface {
// Must be alphanumeric or empty.
Type() string
// Get some property of the Msg.
Get(key interface{}) (value interface{})
// Get the canonical byte representation of the Msg.
GetSignBytes() []byte
@ -63,18 +60,15 @@ Messages can specify basic self-consistency checks using the `ValidateBasic()`
method to enforce that message contents are well formed before any actual logic
begins.
Finally, messages can provide generic access to their contents via `Get(key)`,
but this is mostly for convenience and not type-safe.
For instance, the `Basecoin` message types are defined in `x/bank/tx.go`:
```go
type SendMsg struct {
type MsgSend struct {
Inputs []Input `json:"inputs"`
Outputs []Output `json:"outputs"`
}
type IssueMsg struct {
type MsgIssue struct {
Banker sdk.Address `json:"banker"`
Outputs []Output `json:"outputs"`
}
@ -83,7 +77,7 @@ type IssueMsg struct {
Each specifies the addresses that must sign the message:
```go
func (msg SendMsg) GetSigners() []sdk.Address {
func (msg MsgSend) GetSigners() []sdk.Address {
addrs := make([]sdk.Address, len(msg.Inputs))
for i, in := range msg.Inputs {
addrs[i] = in.Address
@ -91,7 +85,7 @@ func (msg SendMsg) GetSigners() []sdk.Address {
return addrs
}
func (msg IssueMsg) GetSigners() []sdk.Address {
func (msg MsgIssue) GetSigners() []sdk.Address {
return []sdk.Address{msg.Banker}
}
```
@ -174,13 +168,13 @@ property that it can unmarshal into interface types, but it requires the
relevant types to be registered ahead of type. Registration happens on a
`Codec` object, so as not to taint the global name space.
For instance, in `Basecoin`, we wish to register the `SendMsg` and `IssueMsg`
For instance, in `Basecoin`, we wish to register the `MsgSend` and `MsgIssue`
types:
```go
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(bank.SendMsg{}, "cosmos-sdk/SendMsg", nil)
cdc.RegisterConcrete(bank.IssueMsg{}, "cosmos-sdk/IssueMsg", nil)
cdc.RegisterConcrete(bank.MsgSend{}, "cosmos-sdk/MsgSend", nil)
cdc.RegisterConcrete(bank.MsgIssue{}, "cosmos-sdk/MsgIssue", nil)
```
Note how each concrete type is given a name - these name determine the type's
@ -319,8 +313,8 @@ func NewHandler(am sdk.AccountMapper) sdk.Handler {
The quintessential SDK application is Basecoin - a simple
multi-asset cryptocurrency. Basecoin consists of a set of
accounts stored in a Merkle tree, where each account may have
many coins. There are two message types: SendMsg and IssueMsg.
SendMsg allows coins to be sent around, while IssueMsg allows a
many coins. There are two message types: MsgSend and MsgIssue.
MsgSend allows coins to be sent around, while MsgIssue allows a
set of predefined users to issue new coins.
## Conclusion

View File

@ -14,14 +14,13 @@ Welcome to the Cosmos SDK!
SDK
---
.. One maxdepth for now
.. toctree::
:maxdepth: 1
sdk/overview.rst
sdk/install.rst
sdk/glossary.rst
sdk/key-management.rst
.. sdk/overview.rst # needs to be updated
.. old/glossary.rst # not completely up to date but has good content
.. Basecoin
.. --------
@ -29,19 +28,17 @@ SDK
.. .. toctree::
:maxdepth: 2
.. basecoin/basics.rst
.. basecoin/extensions.rst
.. old/basecoin/basics.rst # has a decent getting-start tutorial that's relatively up to date, should be consolidated with the other getting started doc
Extensions
----------
.. Extensions
.. ----------
Replay Protection
~~~~~~~~~~~~~~~~~
.. old/basecoin/extensions.rst # probably not worth salvaging
.. toctree::
:maxdepth: 1
.. Replay Protection
.. ~~~~~~~~~~~~~~~~~
x/replay-protection.rst
.. old/replay-protection.rst # not sure if worth salvaging
Staking
@ -50,17 +47,13 @@ Staking
.. toctree::
:maxdepth: 1
staking/intro.rst
staking/key-management.rst
staking/local-testnet.rst
staking/public-testnet.rst
staking/testnet.rst
.. staking/intro.rst
.. staking/key-management.rst
.. staking/local-testnet.rst
.. staking/public-testnet.rst
Extras
------
.. IBC
.. ---
.. One maxdepth for now
.. toctree::
:maxdepth: 1
ibc.rst
.. old/ibc.rst # needs to be updated

View File

@ -12,7 +12,7 @@ Get Tokens
If you haven't already `created a key <../key-management.html>`__,
do so now. Copy your key's address and enter it into
`this utility <http://www.cosmosvalidators.com/>`__ which will send you
some ``fermion`` testnet tokens.
some ``steak`` testnet tokens.
Get Files
---------
@ -59,6 +59,6 @@ and check our balance:
Where ``$MYADDR`` is the address originally generated by ``gaia keys new bob``.
You are now ready to declare candidacy or delegate some fermions. See the
You are now ready to declare candidacy or delegate some steaks. See the
`staking module overview <./staking-module.html>`__ for more information
on using the ``gaia client``.

View File

@ -15,7 +15,7 @@ while defining as little about that state machine as possible (staying true to t
BaseApp requires stores to be mounted via capabilities keys - handlers can only access
stores they're given the key for. The BaseApp ensures all stores are properly loaded, cached, and committed.
One mounted store is considered the "main" - it holds the latest block header, from which we can find and load the
most recent state.
most recent state ([TODO](https://github.com/cosmos/cosmos-sdk/issues/522)).
BaseApp distinguishes between two handler types - the `AnteHandler` and the `MsgHandler`.
The former is a global validity check (checking nonces, sigs and sufficient balances to pay fees,

View File

@ -1,16 +1,12 @@
Install
=======
If you aren't used to compile go programs and just want the released
version of the code, please head to our
`downloads <https://tendermint.com/download>`__ page to get a
pre-compiled binary for your platform.
Usually, Cosmos SDK can be installed like a normal Go program:
Cosmos SDK can be installed to
``$GOPATH/src/github.com/cosmos/cosmos-sdk`` like a normal Go program:
::
go get -u github.com/cosmos/cosmos-sdk
go get github.com/cosmos/cosmos-sdk
If the dependencies have been updated with breaking changes, or if
another branch is required, ``dep`` is used for dependency management.
@ -20,16 +16,33 @@ repo, the correct way to install is:
::
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
git pull origin master
make all
make get_vendor_deps
make install
make install_examples
This will create the ``basecoin`` binary in ``$GOPATH/bin``.
``make all`` implies ``make get_vendor_deps`` and uses ``dep`` to
install the correct version of all dependencies. It also tests the code,
including some cli tests to make sure your binary behaves properly.
This will install ``gaiad`` and ``gaiacli`` and four example binaries:
``basecoind``, ``basecli``, ``democoind``, and ``democli``.
If you need another branch, make sure to run ``git checkout <branch>``
before ``make all``. And if you switch branches a lot, especially
touching other tendermint repos, you may need to ``make fresh``
sometimes so dep doesn't get confused with all the branches and
versions lying around.
Verify that everything is OK by running:
::
gaiad version
you should see:
::
0.15.0-rc1-9d90c6b
then with:
::
gaiacli version
you should see:
::
0.15.0-rc1-9d90c6b

View File

@ -0,0 +1,18 @@
Key Management
==============
Here we cover many aspects of handling keys within the Cosmos SDK framework.
Pseudo Code
-----------
Generating an address for an ed25519 public key (in pseudo code):
::
const TypeDistinguisher = HexToBytes("1624de6220")
// prepend the TypeDistinguisher as Bytes
SerializedBytes = TypeDistinguisher ++ PubKey.asBytes()
Address = ripemd160(SerializedBytes)

View File

@ -547,7 +547,7 @@ components:
properties:
denom:
type: string
example: fermion
example: steak
amount:
type: number
example: 50

View File

@ -143,9 +143,6 @@ implementing the ``Msg`` interface:
// Must be alphanumeric or empty.
Type() string
// Get some property of the Msg.
Get(key interface{}) (value interface{})
// Get the canonical byte representation of the Msg.
GetSignBytes() []byte
@ -175,9 +172,6 @@ Messages can specify basic self-consistency checks using the ``ValidateBasic()``
method to enforce that message contents are well formed before any actual logic
begins.
Finally, messages can provide generic access to their contents via ``Get(key)``,
but this is mostly for convenience and not type-safe.
For instance, the ``Basecoin`` message types are defined in ``x/bank/tx.go``:
::

View File

@ -1,10 +1,17 @@
# IBC Specification
IBC(Inter-Blockchain Communication) protocol is used by multiple zones on Cosmos. Using IBC, the zones can send coins or arbitrary data to other zones.
The IBC (Inter Blockchain Communication) protocol specifies how tokens,
non-fungible assets and complex objects can be moved securely between different
zones (independent blockchains). IBC is conceptually similar to TCP/IP in the
sense that anyone can implement it in order to be able to establish IBC
connections with willing clients.
## Terms
How IBC module treats incoming IBC packets is simillar with how BaseApp treats incoming transactions. Therefore, the components of IBC module have their corresponding pair in BaseApp.
How IBC module treats incoming IBC packets is similar to how BaseApp treats
incoming transactions. Therefore, the components of IBC module have their
corresponding pair in BaseApp.
| BaseApp Terms | IBC Terms |
| ------------- | ---------- |
@ -12,20 +19,27 @@ How IBC module treats incoming IBC packets is simillar with how BaseApp treats i
| Tx | Packet |
| Msg | Payload |
## MVP Specifications
### [MVP1](./mvp1.md)
MVP1 will contain the basic functionalities, including packet generation and packet receivement. There will be no security check for incoming packets.
MVP1 will contain the basic functionalities, including packet generation and
incoming packet processing. There will be no security check for incoming
packets.
### [MVP2](./mvp2.md)
IBC module will be more modular in MVP2. Indivisual modules can register custom handlers to IBC module.
The IBC module will be more modular in MVP2. Individual modules can register
custom handlers on the IBC module.
### [MVP3](./mvp3.md)
Light client verification is added to verify the message from the other chain. Registering chains with their ROT(Root Of Trust) is needed.
Light client verification is added to verify an IBC packet from another chain.
Registering chains with their RoT(Root of Trust) is added as well.
### [MVP4](./mvp4.md)
ACK verification / timeout handler helper functions and messaging queue are implemented to make it failsafe. Callbacks will be registered to the dispatcher to handle failure when they register handlers.
ACK verification / timeout handler helper functions and messaging queues are
implemented to make it safe. Callbacks will be registered to the dispatcher to
handle failure when they register handlers.

View File

@ -1,77 +1,205 @@
Using Gaia
==========
Using The Staking Module
========================
This project is a demonstration of the Cosmos Hub with staking functionality; it is
designed to get validator acquianted with staking concepts and procedure.
This project is a demonstration of the Cosmos Hub staking functionality; it is
designed to get validator acquianted with staking concepts and procedures.
Potential validators will be declaring their candidacy, after which users can
delegate and, if they so wish, unbond. This can be practiced using a local or
public testnet.
This example covers initial setup of a two-node testnet between a server in the cloud and a local machine. Begin this tutorial from a cloud machine that you've ``ssh``'d into.
Install
-------
The ``gaia`` tooling is an extension of the Cosmos-SDK; to install:
The ``gaiad`` and ``gaiacli`` binaries:
::
go get github.com/cosmos/gaia
cd $GOPATH/src/github.com/cosmos/gaia
go get github.com/cosmos/cosmos-sdk
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
make get_vendor_deps
make install
It has three primary commands:
Let's jump right into it. First, we initialize some default files:
::
Available Commands:
node The Cosmos Network delegation-game blockchain test
rest-server REST client for gaia commands
client Gaia light client
version Show version info
help Help about any command
gaiad init
and a handful of flags that are highlighted only as necessary.
which will output:
The ``gaia node`` command is a proxt for running a tendermint node. You'll be using
this command to either initialize a new node, or - using existing files - joining
the testnet.
::
The ``gaia rest-server`` command is used by the `cosmos UI <https://github.com/cosmos/cosmos-ui>`__.
I[03-30|11:20:13.365] Found private validator module=main path=/root/.gaiad/config/priv_validator.json
I[03-30|11:20:13.365] Found genesis file module=main path=/root/.gaiad/config/genesis.json
Secret phrase to access coins:
citizen hungry tennis noise park hire glory exercise link glow dolphin labor design grit apple abandon
Lastly, the ``gaia client`` command is the workhorse of the staking module. It allows
for sending various transactions and other types of interaction with a running chain.
that you've setup or joined a testnet.
This tell us we have a ``priv_validator.json`` and ``genesis.json`` in the ``~/.gaiad/config`` directory. A ``config.toml`` was also created in the same directory. It is a good idea to get familiar with those files. Write down the seed.
Generating Keys
---------------
The next thing we'll need to is add the key from ``priv_validator.json`` to the ``gaiacli`` key manager. For this we need a seed and a password:
Review the `key management tutorial <../key-management.html>`__ and create one key
if you'll be joining the public testnet, and three keys if you'll be trying out a local
testnet.
::
gaiacli keys add alice --recover
which will give you three prompts:
::
Enter a passphrase for your key:
Repeat the passphrase:
Enter your recovery seed phrase:
create a password and copy in your seed phrase. The name and address of the key will be output:
::
NAME: ADDRESS: PUBKEY:
alice 67997DD03D527EB439B7193F2B813B05B219CC02 1624DE6220BB89786C1D597050438C728202436552C6226AB67453CDB2A4D2703402FB52B6
You can see all available keys with:
::
gaiacli keys list
Setup Testnet
-------------
The first thing you'll want to do is either `create a local testnet <./local-testnet.html>`__ or
join a `public testnet <./public-testnet.html>`__. Either step is required before proceeding.
Next, we start the daemon (do this in another window):
The rest of this tutorial will assume a local testnet with three participants: ``alice`` will be
the initial validator, ``bob`` will first receives tokens from ``alice`` then declare candidacy
as a validator, and ``charlie`` will bond then unbond to ``bob``. If you're joining the public
testnet, the token amounts will need to be adjusted.
::
gaiad start
and you'll see blocks start streaming through.
For this example, we're doing the above on a cloud machine. The next steps should be done on your local machine or another server in the cloud, which will join the running testnet then bond/unbond.
Accounts
--------
We have:
- ``alice`` the initial validator (in the cloud)
- ``bob`` receives tokens from ``alice`` then declares candidacy (from local machine)
- ``charlie`` will bond and unbond to ``bob`` (from local machine)
Remember that ``alice`` was already created. On your second machine, install the binaries and create two new keys:
::
gaiacli keys add bob
gaiacli keys add charlie
both of which will prompt you for a password. Now we need to copy the ``genesis.json`` and ``config.toml`` from the first machine (with ``alice``) to the second machine. This is a good time to look at both these files.
The ``genesis.json`` should look something like:
::
{
"app_state": {
"accounts": [
{
"address": "1D9B2356CAADF46D3EE3488E3CCE3028B4283DEE",
"coins": [
{
"denom": "steak",
"amount": 100000
}
]
}
],
"stake": {
"pool": {
"total_supply": 0,
"bonded_shares": {
"num": 0,
"denom": 1
},
"unbonded_shares": {
"num": 0,
"denom": 1
},
"bonded_pool": 0,
"unbonded_pool": 0,
"inflation_last_time": 0,
"inflation": {
"num": 7,
"denom": 100
}
},
"params": {
"inflation_rate_change": {
"num": 13,
"denom": 100
},
"inflation_max": {
"num": 20,
"denom": 100
},
"inflation_min": {
"num": 7,
"denom": 100
},
"goal_bonded": {
"num": 67,
"denom": 100
},
"max_validators": 100,
"bond_denom": "steak"
}
}
},
"validators": [
{
"pub_key": {
"type": "AC26791624DE60",
"value": "rgpc/ctVld6RpSfwN5yxGBF17R1PwMTdhQ9gKVUZp5g="
},
"power": 10,
"name": ""
}
],
"app_hash": "",
"genesis_time": "0001-01-01T00:00:00Z",
"chain_id": "test-chain-Uv1EVU"
}
To notice is that the ``accounts`` field has a an address and a whole bunch of "mycoin". This is ``alice``'s address (todo: dbl check). Under ``validators`` we see the ``pub_key.data`` field, which will match the same field in the ``priv_validator.json`` file.
The ``config.toml`` is long so let's focus on one field:
::
# Comma separated list of seed nodes to connect to
seeds = ""
On the ``alice`` cloud machine, we don't need to do anything here. Instead, we need its IP address. After copying this file (and the ``genesis.json`` to your local machine, you'll want to put the IP in the ``seeds = "138.197.161.74"`` field, in this case, we have a made-up IP. For joining testnets with many nodes, you can add more comma-seperated IPs to the list.
Now that your files are all setup, it's time to join the network. On your local machine, run:
::
gaiad start
and your new node will connect to the running validator (``alice``).
Sending Tokens
--------------
We'll have ``alice`` who is currently quite rich, send some ``fermions`` to ``bob``:
We'll have ``alice`` send some ``mycoin`` to ``bob``, who has now joined the network:
::
gaia client tx send --amount=1000fermion --sequence=1 --name=alice --to=5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6
gaiacli send --amount=1000mycoin --sequence=0 --name=alice --to=5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6 --chain-id=test-chain-Uv1EVU
where the ``--sequence`` flag is to be incremented for each transaction, the ``--name`` flag names the sender, and the ``--to`` flag takes ``bob``'s address. You'll see something like:
where the ``--sequence`` flag is to be incremented for each transaction, the ``--name`` flag is the sender (alice), and the ``--to`` flag takes ``bob``'s address. You'll see something like:
::
@ -101,19 +229,25 @@ where the ``--sequence`` flag is to be incremented for each transaction, the ``-
"height": 2963
}
Check out ``bob``'s account, which should now have 992 fermions:
TODO: check the above with current actual output.
Check out ``bob``'s account, which should now have 1000 mycoin:
::
gaia client query account 5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6
gaiacli account 5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6
Adding a Second Validator
-------------------------
**This section is wrong/needs to be updated**
Next, let's add the second node as a validator.
First, we need the pub_key data:
** need to make bob a priv_Val above?
::
cat $HOME/.gaia2/priv_validator.json
@ -130,7 +264,7 @@ Now ``bob`` can declare candidacy to that pubkey:
::
gaia client tx declare-candidacy --amount=10fermion --name=bob --pubkey=<pub_key data> --moniker=bobby
gaiacli declare-candidacy --amount=10mycoin --name=bob --pubkey=<pub_key data> --moniker=bobby
with an output like:
@ -147,11 +281,11 @@ with an output like:
}
We should see ``bob``'s account balance decrease by 10 fermions:
We should see ``bob``'s account balance decrease by 10 mycoin:
::
gaia client query account 5D93A6059B6592833CBC8FA3DA90EE0382198985
gaiacli account 5D93A6059B6592833CBC8FA3DA90EE0382198985
To confirm for certain the new validator is active, ask the tendermint node:
@ -163,7 +297,7 @@ If you now kill either node, blocks will stop streaming in, because
there aren't enough validators online. Turn it back on and they will
start streaming again.
Now that ``bob`` has declared candidacy, which essentially bonded 10 fermions and made him a validator, we're going to get ``charlie`` to delegate some coins to ``bob``.
Now that ``bob`` has declared candidacy, which essentially bonded 10 mycoin and made him a validator, we're going to get ``charlie`` to delegate some coins to ``bob``.
Delegating
----------
@ -172,13 +306,13 @@ First let's have ``alice`` send some coins to ``charlie``:
::
gaia client tx send --amount=1000fermion --sequence=2 --name=alice --to=48F74F48281C89E5E4BE9092F735EA519768E8EF
gaiacli tx --amount=1000mycoin --sequence=2 --name=alice --to=48F74F48281C89E5E4BE9092F735EA519768E8EF
Then ``charlie`` will delegate some fermions to ``bob``:
Then ``charlie`` will delegate some mycoin to ``bob``:
::
gaia client tx delegate --amount=10fermion --name=charlie --pubkey=<pub_key data>
gaiacli tx delegate --amount=10mycoin --name=charlie --pubkey=<pub_key data>
You'll see output like:
@ -194,13 +328,13 @@ You'll see output like:
"height": 51585
}
And that's it. You can query ``charlie``'s account to see the decrease in fermions.
And that's it. You can query ``charlie``'s account to see the decrease in mycoin.
To get more information about the candidate, try:
::
gaia client query candidate --pubkey=<pub_key data>
gaiacli query candidate --pubkey=<pub_key data>
and you'll see output similar to:
@ -233,7 +367,7 @@ It's also possible the query the delegator's bond like so:
::
gaia client query delegator-bond --delegator-address 48F74F48281C89E5E4BE9092F735EA519768E8EF --pubkey 52D6FCD8C92A97F7CCB01205ADF310A18411EA8FDCC10E65BF2FCDB05AD1689B
gaiacli query delegator-bond --delegator-address 48F74F48281C89E5E4BE9092F735EA519768E8EF --pubkey 52D6FCD8C92A97F7CCB01205ADF310A18411EA8FDCC10E65BF2FCDB05AD1689B
with an output similar to:
@ -262,9 +396,7 @@ your VotingPower reduce and your account balance increase.
::
gaia client tx unbond --amount=5fermion --name=charlie --pubkey=<pub_key data>
gaia client query account 48F74F48281C89E5E4BE9092F735EA519768E8EF
gaiacli unbond --amount=5mycoin --name=charlie --pubkey=<pub_key data>
gaiacli account 48F74F48281C89E5E4BE9092F735EA519768E8EF
See the bond decrease with ``gaia client query delegator-bond`` like above.
That concludes an overview of the ``gaia`` tooling for local testing.
See the bond decrease with ``gaiacli query delegator-bond`` like above.

82
docs/staking/testnet.rst Normal file
View File

@ -0,0 +1,82 @@
Testnet Setup
=============
**Note:** This document is incomplete and may not be up-to-date with the state of the code.
See the `installation guide <../sdk/install.html>`__ for details on installation.
Here is a quick example to get you off your feet:
First, generate a couple of genesis transactions to be incorparated into the genesis file, this will create two keys with the password ``1234567890``
::
gaiad init gen-tx --name=foo --home=$HOME/.gaiad1
gaiad init gen-tx --name=bar --home=$HOME/.gaiad2
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.
Now let's actually create the genesis files for both nodes:
::
cp -a ~/.gaiad2/config/gentx/. ~/.gaiad1/config/gentx/
cp -a ~/.gaiad1/config/gentx/. ~/.gaiad2/config/gentx/
gaiad init --gen-txs --home=$HOME/.gaiad1 --chain-id=test-chain
gaiad init --gen-txs --home=$HOME/.gaiad2 --chain-id=test-chain
**Note:** If you've already run these tests you may need to overwrite genesis using the ``-o`` flag
What we just did is copy the genesis transactions between each of the nodes so there is a common genesis transaction set; then we created both genesis files independently from each home directory. Importantly both nodes have independently created their ``genesis.json`` and ``config.toml`` files, which should be identical between nodes.
Great, now that we've initialized the chains, we can start both nodes in the background:
::
gaiad start --home=$HOME/.gaiad1 &> gaia1.log &
NODE1_PID=$!
gaia start --home=$HOME/.gaiad2 &> gaia2.log &
NODE2_PID=$!
Note that we save the PID so we can later kill the processes. You can peak at your logs with ``tail gaia1.log``, or follow them for a bit with ``tail -f gaia1.log``.
Nice. We can also lookup the validator set:
::
gaiacli validatorset
Then, we try to transfer some ``steak`` to another account:
::
gaiacli account <FOO-ADDR>
gaiacli account <BAR-ADDR>
gaiacli send --amount=10steak --to=<BAR-ADDR> --name=foo --chain-id=test-chain
**Note:** We need to be careful with the ``chain-id`` and ``sequence``
Check the balance & sequence with:
::
gaiacli account <BAR-ADDR>
To confirm for certain the new validator is active, check tendermint:
::
curl localhost:46657/validators
Finally, to relinquish all your power, unbond some coins. You should see your VotingPower reduce and your account balance increase.
::
gaiacli unbond --chain-id=<chain-id> --name=test
That's it!
**Note:** TODO demonstrate edit-candidacy
**Note:** TODO demonstrate delegation
**Note:** TODO demonstrate unbond of delegation
**Note:** TODO demonstrate unbond candidate

View File

@ -14,7 +14,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/simplestake"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
)
@ -29,16 +29,16 @@ type BasecoinApp struct {
cdc *wire.Codec
// keys to access the substores
capKeyMainStore *sdk.KVStoreKey
capKeyAccountStore *sdk.KVStoreKey
capKeyIBCStore *sdk.KVStoreKey
capKeyStakingStore *sdk.KVStoreKey
keyMain *sdk.KVStoreKey
keyAccount *sdk.KVStoreKey
keyIBC *sdk.KVStoreKey
keyStake *sdk.KVStoreKey
// Manage getting and setting accounts
accountMapper sdk.AccountMapper
// Handle fees
feeHandler sdk.FeeHandler
coinKeeper bank.Keeper
ibcMapper ibc.Mapper
stakeKeeper stake.Keeper
}
func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
@ -48,92 +48,64 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
// Create your application object.
var app = &BasecoinApp{
BaseApp: bam.NewBaseApp(appName, logger, db),
cdc: cdc,
capKeyMainStore: sdk.NewKVStoreKey("main"),
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
capKeyStakingStore: 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"),
}
// Define the accountMapper.
app.accountMapper = auth.NewAccountMapper(
cdc,
app.capKeyMainStore, // target store
app.keyAccount, // target store
&types.AppAccount{}, // prototype
).Seal()
)
// Add handlers.
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
// add accountMapper/handlers
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))
// register message routes
app.Router().
AddRoute("bank", bank.NewHandler(coinKeeper)).
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
// Define the feeHandler.
app.feeHandler = auth.BurnFeeHandler
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
AddRoute("stake", stake.NewHandler(app.stakeKeeper))
// Initialize BaseApp.
app.SetTxDecoder(app.txDecoder)
app.SetInitChainer(app.initChainer)
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeyIBCStore, app.capKeyStakingStore)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeHandler))
err := app.LoadLatestVersion(app.capKeyMainStore)
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, auth.BurnFeeHandler))
err := app.LoadLatestVersion(app.keyMain)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
// Custom tx codec
func MakeCodec() *wire.Codec {
var cdc = wire.NewCodec()
wire.RegisterCrypto(cdc) // Register crypto.
sdk.RegisterWire(cdc) // Register Msgs
bank.RegisterWire(cdc)
stake.RegisterWire(cdc)
ibc.RegisterWire(cdc)
// Register Msgs
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(bank.SendMsg{}, "basecoin/Send", nil)
cdc.RegisterConcrete(bank.IssueMsg{}, "basecoin/Issue", nil)
cdc.RegisterConcrete(ibc.IBCTransferMsg{}, "basecoin/IBCTransferMsg", nil)
cdc.RegisterConcrete(ibc.IBCReceiveMsg{}, "basecoin/IBCReceiveMsg", nil)
cdc.RegisterConcrete(simplestake.BondMsg{}, "basecoin/BondMsg", nil)
cdc.RegisterConcrete(simplestake.UnbondMsg{}, "basecoin/UnbondMsg", nil)
// Register AppAccount
// register custom AppAccount
cdc.RegisterInterface((*sdk.Account)(nil), nil)
cdc.RegisterConcrete(&types.AppAccount{}, "basecoin/Account", nil)
// Register crypto.
wire.RegisterCrypto(cdc)
return cdc
}
// Custom logic for transaction decoding
func (app *BasecoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx = sdk.StdTx{}
if len(txBytes) == 0 {
return nil, sdk.ErrTxDecode("txBytes are empty")
}
// StdTx.Msg is an interface. The concrete types
// are registered by MakeTxCodec in bank.RegisterAmino.
err := app.cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
}
return tx, nil
}
// Custom logic for basecoin initialization
func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes
genesisState := new(types.GenesisState)
err := json.Unmarshal(stateJSON, genesisState)
err := app.cdc.UnmarshalJSON(stateJSON, genesisState)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
@ -149,3 +121,25 @@ func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain)
}
return abci.ResponseInitChain{}
}
// Custom logic for state export
func (app *BasecoinApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
ctx := app.NewContext(true, abci.Header{})
// iterate to get the accounts
accounts := []*types.GenesisAccount{}
appendAccount := func(acc sdk.Account) (stop bool) {
account := &types.GenesisAccount{
Address: acc.GetAddress(),
Coins: acc.GetCoins(),
}
accounts = append(accounts, account)
return false
}
app.accountMapper.IterateAccounts(ctx, appendAccount)
genState := types.GenesisState{
Accounts: accounts,
}
return wire.MarshalJSONIndent(app.cdc, genState)
}

View File

@ -42,12 +42,12 @@ var (
0,
}
sendMsg1 = bank.SendMsg{
sendMsg1 = bank.MsgSend{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
}
sendMsg2 = bank.SendMsg{
sendMsg2 = bank.MsgSend{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{
bank.NewOutput(addr2, halfCoins),
@ -55,7 +55,7 @@ var (
},
}
sendMsg3 = bank.SendMsg{
sendMsg3 = bank.MsgSend{
Inputs: []bank.Input{
bank.NewInput(addr1, coins),
bank.NewInput(addr4, coins),
@ -66,7 +66,7 @@ var (
},
}
sendMsg4 = bank.SendMsg{
sendMsg4 = bank.MsgSend{
Inputs: []bank.Input{
bank.NewInput(addr2, coins),
},
@ -75,7 +75,7 @@ var (
},
}
sendMsg5 = bank.SendMsg{
sendMsg5 = bank.MsgSend{
Inputs: []bank.Input{
bank.NewInput(addr1, manyCoins),
},
@ -166,7 +166,7 @@ func TestSortGenesis(t *testing.T) {
// Unsorted coins means invalid
err := sendMsg5.ValidateBasic()
require.Equal(t, sdk.CodeInvalidCoins, err.ABCICode(), err.ABCILog())
require.Equal(t, sdk.CodeInvalidCoins, err.Code(), err.ABCILog())
// Sort coins, should be valid
sendMsg5.Inputs[0].Coins.Sort()
@ -208,7 +208,7 @@ func TestGenesis(t *testing.T) {
assert.Equal(t, acc, res1)
}
func TestSendMsgWithAccounts(t *testing.T) {
func TestMsgSendWithAccounts(t *testing.T) {
bapp := newBasecoinApp()
// Construct some genesis bytes to reflect basecoin/types/AppAccount
@ -243,13 +243,13 @@ func TestSendMsgWithAccounts(t *testing.T) {
tx.Signatures[0].Sequence = 1
res := bapp.Deliver(tx)
assert.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
// resigning the tx with the bumped sequence should work
SignCheckDeliver(t, bapp, sendMsg1, []int64{1}, true, priv1)
}
func TestSendMsgMultipleOut(t *testing.T) {
func TestMsgSendMultipleOut(t *testing.T) {
bapp := newBasecoinApp()
genCoins, err := sdk.ParseCoins("42foocoin")
@ -311,7 +311,7 @@ func TestSengMsgMultipleInOut(t *testing.T) {
CheckBalance(t, bapp, addr3, "10foocoin")
}
func TestSendMsgDependent(t *testing.T) {
func TestMsgSendDependent(t *testing.T) {
bapp := newBasecoinApp()
genCoins, err := sdk.ParseCoins("42foocoin")
@ -339,7 +339,7 @@ func TestSendMsgDependent(t *testing.T) {
CheckBalance(t, bapp, addr1, "42foocoin")
}
func TestQuizMsg(t *testing.T) {
func TestMsgQuiz(t *testing.T) {
bapp := newBasecoinApp()
// Construct genesis state
@ -437,18 +437,18 @@ func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq []int64,
// Run a Check
res := bapp.Check(tx)
if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
}
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx)
if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
}
bapp.EndBlock(abci.RequestEndBlock{})
//bapp.Commit()

View File

@ -14,10 +14,10 @@ import (
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands"
simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands"
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"
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
@ -51,24 +51,18 @@ func main() {
// add query/post commands (custom to binary)
rootCmd.AddCommand(
client.GetCommands(
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)),
)...)
rootCmd.AddCommand(
client.PostCommands(
bankcmd.SendTxCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCTransferCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCRelayCmd(cdc),
simplestakingcmd.BondTxCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
simplestakingcmd.UnbondTxCmd(cdc),
stakecmd.GetCmdDeclareCandidacy(cdc),
stakecmd.GetCmdEditCandidacy(cdc),
stakecmd.GetCmdDelegate(cdc),
stakecmd.GetCmdUnbond(cdc),
)...)
// add proxy, version and key info

View File

@ -1,8 +1,8 @@
package main
import (
"encoding/json"
"os"
"path/filepath"
"github.com/spf13/cobra"
@ -15,31 +15,31 @@ import (
"github.com/cosmos/cosmos-sdk/server"
)
// rootCmd is the entry point for this binary
var (
context = server.NewDefaultContext()
rootCmd = &cobra.Command{
func main() {
cdc := app.MakeCodec()
ctx := server.NewDefaultContext()
rootCmd := &cobra.Command{
Use: "basecoind",
Short: "Basecoin Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(context),
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
)
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
dataDir := filepath.Join(rootDir, "data")
db, err := dbm.NewGoLevelDB("basecoin", dataDir)
if err != nil {
return nil, err
}
bapp := app.NewBasecoinApp(logger, db)
return bapp, nil
}
func main() {
server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context)
server.AddCommands(ctx, cdc, rootCmd, server.DefaultAppInit,
server.ConstructAppCreator(newApp, "basecoin"),
server.ConstructAppExporter(exportAppState, "basecoin"))
// prepare and add flags
rootDir := os.ExpandEnv("$HOME/.basecoind")
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
executor.Execute()
}
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) {
bapp := app.NewBasecoinApp(logger, db)
return bapp.ExportAppStateJSON()
}

View File

@ -14,11 +14,11 @@ 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/simplestake"
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/simplestake"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/sketchy"
)
@ -38,11 +38,15 @@ type DemocoinApp struct {
capKeyIBCStore *sdk.KVStoreKey
capKeyStakingStore *sdk.KVStoreKey
// keepers
coinKeeper bank.Keeper
coolKeeper cool.Keeper
powKeeper pow.Keeper
ibcMapper ibc.Mapper
stakeKeeper simplestake.Keeper
// Manage getting and setting accounts
accountMapper sdk.AccountMapper
// Handle fees
feeHandler sdk.FeeHandler
}
func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
@ -52,7 +56,7 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
// Create your application object.
var app = &DemocoinApp{
BaseApp: bam.NewBaseApp(appName, logger, db),
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
cdc: cdc,
capKeyMainStore: sdk.NewKVStoreKey("main"),
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
@ -64,90 +68,59 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
// Define the accountMapper.
app.accountMapper = auth.NewAccountMapper(
cdc,
app.capKeyMainStore, // target store
&types.AppAccount{}, // prototype
).Seal()
app.capKeyAccountStore, // target store
&types.AppAccount{}, // prototype
)
// Add handlers.
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper)
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
app.coinKeeper = bank.NewKeeper(app.accountMapper)
app.coolKeeper = cool.NewKeeper(app.capKeyMainStore, app.coinKeeper, app.RegisterCodespace(cool.DefaultCodespace))
app.powKeeper = pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), app.coinKeeper, app.RegisterCodespace(pow.DefaultCodespace))
app.ibcMapper = ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace))
app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace))
app.Router().
AddRoute("bank", bank.NewHandler(coinKeeper)).
AddRoute("cool", cool.NewHandler(coolKeeper)).
AddRoute("pow", powKeeper.Handler).
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
AddRoute("cool", cool.NewHandler(app.coolKeeper)).
AddRoute("pow", app.powKeeper.Handler).
AddRoute("sketchy", sketchy.NewHandler()).
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
// Define the feeHandler.
app.feeHandler = auth.BurnFeeHandler
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper))
// Initialize BaseApp.
app.SetTxDecoder(app.txDecoder)
app.SetInitChainer(app.initChainerFn(coolKeeper, powKeeper))
app.SetInitChainer(app.initChainerFn(app.coolKeeper, app.powKeeper))
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeyPowStore, app.capKeyIBCStore, app.capKeyStakingStore)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeHandler))
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, auth.BurnFeeHandler))
err := app.LoadLatestVersion(app.capKeyMainStore)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
// custom tx codec
func MakeCodec() *wire.Codec {
var cdc = wire.NewCodec()
// Register Msgs
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(bank.SendMsg{}, "democoin/Send", nil)
cdc.RegisterConcrete(bank.IssueMsg{}, "democoin/Issue", nil)
cdc.RegisterConcrete(cool.QuizMsg{}, "democoin/Quiz", nil)
cdc.RegisterConcrete(cool.SetTrendMsg{}, "democoin/SetTrend", nil)
cdc.RegisterConcrete(pow.MineMsg{}, "democoin/Mine", nil)
cdc.RegisterConcrete(ibc.IBCTransferMsg{}, "democoin/IBCTransferMsg", nil)
cdc.RegisterConcrete(ibc.IBCReceiveMsg{}, "democoin/IBCReceiveMsg", nil)
cdc.RegisterConcrete(simplestake.BondMsg{}, "democoin/BondMsg", nil)
cdc.RegisterConcrete(simplestake.UnbondMsg{}, "democoin/UnbondMsg", nil)
wire.RegisterCrypto(cdc) // Register crypto.
sdk.RegisterWire(cdc) // Register Msgs
cool.RegisterWire(cdc)
pow.RegisterWire(cdc)
bank.RegisterWire(cdc)
ibc.RegisterWire(cdc)
simplestake.RegisterWire(cdc)
// Register AppAccount
cdc.RegisterInterface((*sdk.Account)(nil), nil)
cdc.RegisterConcrete(&types.AppAccount{}, "democoin/Account", nil)
// Register crypto.
wire.RegisterCrypto(cdc)
return cdc
}
// custom logic for transaction decoding
func (app *DemocoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx = sdk.StdTx{}
if len(txBytes) == 0 {
return nil, sdk.ErrTxDecode("txBytes are empty")
}
// StdTx.Msg is an interface. The concrete types
// are registered by MakeTxCodec in bank.RegisterWire.
err := app.cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
}
return tx, nil
}
// custom logic for democoin initialization
func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes
genesisState := new(types.GenesisState)
err := json.Unmarshal(stateJSON, genesisState)
err := app.cdc.UnmarshalJSON(stateJSON, genesisState)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
@ -163,13 +136,13 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep
}
// Application specific genesis handling
err = coolKeeper.InitGenesis(ctx, genesisState.CoolGenesis)
err = cool.InitGenesis(ctx, app.coolKeeper, genesisState.CoolGenesis)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
}
err = powKeeper.InitGenesis(ctx, genesisState.PowGenesis)
err = pow.InitGenesis(ctx, app.powKeeper, genesisState.POWGenesis)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
@ -178,3 +151,27 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep
return abci.ResponseInitChain{}
}
}
// Custom logic for state export
func (app *DemocoinApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
ctx := app.NewContext(true, abci.Header{})
// iterate to get the accounts
accounts := []*types.GenesisAccount{}
appendAccount := func(acc sdk.Account) (stop bool) {
account := &types.GenesisAccount{
Address: acc.GetAddress(),
Coins: acc.GetCoins(),
}
accounts = append(accounts, account)
return false
}
app.accountMapper.IterateAccounts(ctx, appendAccount)
genState := types.GenesisState{
Accounts: accounts,
POWGenesis: pow.WriteGenesis(ctx, app.powKeeper),
CoolGenesis: cool.WriteGenesis(ctx, app.coolKeeper),
}
return wire.MarshalJSONIndent(app.cdc, genState)
}

View File

@ -36,32 +36,32 @@ var (
0,
}
sendMsg = bank.SendMsg{
sendMsg = bank.MsgSend{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
}
quizMsg1 = cool.QuizMsg{
quizMsg1 = cool.MsgQuiz{
Sender: addr1,
CoolAnswer: "icecold",
}
quizMsg2 = cool.QuizMsg{
quizMsg2 = cool.MsgQuiz{
Sender: addr1,
CoolAnswer: "badvibesonly",
}
setTrendMsg1 = cool.SetTrendMsg{
setTrendMsg1 = cool.MsgSetTrend{
Sender: addr1,
Cool: "icecold",
}
setTrendMsg2 = cool.SetTrendMsg{
setTrendMsg2 = cool.MsgSetTrend{
Sender: addr1,
Cool: "badvibesonly",
}
setTrendMsg3 = cool.SetTrendMsg{
setTrendMsg3 = cool.MsgSetTrend{
Sender: addr1,
Cool: "warmandkind",
}
@ -157,7 +157,7 @@ func TestGenesis(t *testing.T) {
assert.Equal(t, acc, res1)
}
func TestSendMsgWithAccounts(t *testing.T) {
func TestMsgSendWithAccounts(t *testing.T) {
bapp := newDemocoinApp()
// Construct some genesis bytes to reflect democoin/types/AppAccount
@ -202,12 +202,12 @@ func TestSendMsgWithAccounts(t *testing.T) {
// Run a Check
res := bapp.Check(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
// Check balances
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
@ -218,22 +218,22 @@ func TestSendMsgWithAccounts(t *testing.T) {
// Delivering again should cause replay error
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeInvalidSequence, res.Code, res.Log)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeInvalidSequence), sdk.ABCICodeType(res.Code), res.Log)
// bumping the txnonce number without resigning should be an auth error
tx.Signatures[0].Sequence = 1
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), sdk.ABCICodeType(res.Code), res.Log)
// resigning the tx with the bumped sequence should work
sequences = []int64{1}
sig = priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, tx.Msg))
tx.Signatures[0].Signature = sig
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
}
func TestMineMsg(t *testing.T) {
func TestMsgMine(t *testing.T) {
bapp := newDemocoinApp()
// Construct genesis state
@ -271,11 +271,11 @@ func TestMineMsg(t *testing.T) {
assert.Equal(t, acc1, res1)
// Mine and check for reward
mineMsg1 := pow.GenerateMineMsg(addr1, 1, 2)
mineMsg1 := pow.GenerateMsgMine(addr1, 1, 2)
SignCheckDeliver(t, bapp, mineMsg1, 0, true)
CheckBalance(t, bapp, "1pow")
// Mine again and check for reward
mineMsg2 := pow.GenerateMineMsg(addr1, 2, 3)
mineMsg2 := pow.GenerateMsgMine(addr1, 2, 3)
SignCheckDeliver(t, bapp, mineMsg2, 1, true)
CheckBalance(t, bapp, "2pow")
// Mine again - should be invalid
@ -284,7 +284,7 @@ func TestMineMsg(t *testing.T) {
}
func TestQuizMsg(t *testing.T) {
func TestMsgQuiz(t *testing.T) {
bapp := newDemocoinApp()
// Construct genesis state
@ -403,18 +403,18 @@ func SignCheckDeliver(t *testing.T, bapp *DemocoinApp, msg sdk.Msg, seq int64, e
// Run a Check
res := bapp.Check(tx)
if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
}
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx)
if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
}
bapp.EndBlock(abci.RequestEndBlock{})
//bapp.Commit()

View File

@ -14,15 +14,15 @@ import (
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands"
simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands"
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"
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
coolcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool/commands"
powcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/pow/commands"
coolcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool/client/cli"
powcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/pow/client/cli"
simplestakingcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/simplestake/client/cli"
)
// rootCmd is the entry point for this binary
@ -54,7 +54,7 @@ func main() {
// start with commands common to basecoin
rootCmd.AddCommand(
client.GetCommands(
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)),
)...)
rootCmd.AddCommand(
client.PostCommands(

View File

@ -3,7 +3,6 @@ package main
import (
"encoding/json"
"os"
"path/filepath"
"github.com/spf13/cobra"
@ -14,49 +13,57 @@ import (
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
// rootCmd is the entry point for this binary
var (
context = server.NewDefaultContext()
rootCmd = &cobra.Command{
Use: "democoind",
Short: "Democoin Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(context),
}
)
// defaultAppState sets up the app_state for the
// default genesis file
func defaultAppState(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
baseJSON, err := server.DefaultGenAppState(args, addr, coinDenom)
if err != nil {
return nil, err
}
var jsonMap map[string]json.RawMessage
err = json.Unmarshal(baseJSON, &jsonMap)
if err != nil {
return nil, err
}
jsonMap["cool"] = json.RawMessage(`{
"trend": "ice-cold"
}`)
bz, err := json.Marshal(jsonMap)
return json.RawMessage(bz), err
// init parameters
var CoolAppInit = server.AppInit{
AppGenState: CoolAppGenState,
AppGenTx: server.SimpleAppGenTx,
}
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
db, err := dbm.NewGoLevelDB("democoin", filepath.Join(rootDir, "data"))
// coolGenAppParams sets up the app_state and appends the cool app state
func CoolAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
appState, err = server.SimpleAppGenState(cdc, appGenTxs)
if err != nil {
return nil, err
return
}
bapp := app.NewDemocoinApp(logger, db)
return bapp, nil
key := "cool"
value := json.RawMessage(`{
"trend": "ice-cold"
}`)
appState, err = server.AppendJSON(cdc, appState, key, value)
key = "pow"
value = json.RawMessage(`{
"difficulty": 1,
"count": 0
}`)
appState, err = server.AppendJSON(cdc, appState, key, value)
return
}
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) {
dapp := app.NewDemocoinApp(logger, db)
return dapp.ExportAppStateJSON()
}
func main() {
server.AddCommands(rootCmd, defaultAppState, generateApp, context)
cdc := app.MakeCodec()
ctx := server.NewDefaultContext()
rootCmd := &cobra.Command{
Use: "democoind",
Short: "Democoin Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
}
server.AddCommands(ctx, cdc, rootCmd, CoolAppInit,
server.ConstructAppCreator(newApp, "democoin"),
server.ConstructAppExporter(exportAppState, "democoin"))
// prepare and add flags
rootDir := os.ExpandEnv("$HOME/.democoind")

View File

@ -45,8 +45,8 @@ func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder {
// State to Unmarshal
type GenesisState struct {
Accounts []*GenesisAccount `json:"accounts"`
PowGenesis pow.PowGenesis `json:"pow"`
CoolGenesis cool.CoolGenesis `json:"cool"`
POWGenesis pow.Genesis `json:"pow"`
CoolGenesis cool.Genesis `json:"cool"`
}
// GenesisAccount doesn't need pubkey or sequence

View File

@ -1,16 +1,15 @@
package commands
package cli
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/wire"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
)
@ -20,11 +19,8 @@ func QuizTxCmd(cdc *wire.Codec) *cobra.Command {
return &cobra.Command{
Use: "cool [answer]",
Short: "What's cooler than being cool?",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide an answer")
}
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
// get the from address from the name flag
@ -34,19 +30,13 @@ func QuizTxCmd(cdc *wire.Codec) *cobra.Command {
}
// create the message
msg := cool.NewQuizMsg(from, args[0])
msg := cool.NewMsgQuiz(from, args[0])
// get account name
name := viper.GetString(client.FlagName)
// default to next sequence number if none provided
ctx, err = context.EnsureSequence(ctx)
if err != nil {
return err
}
// build and sign the transaction, then broadcast to Tendermint
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
res, err := ctx.EnsureSignBuildBroadcast(name, msg, cdc)
if err != nil {
return err
}
@ -62,11 +52,8 @@ func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command {
return &cobra.Command{
Use: "setcool [answer]",
Short: "You're so cool, tell us what is cool!",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide an answer")
}
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
// get the from address from the name flag
@ -78,17 +65,11 @@ func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command {
// get account name
name := viper.GetString(client.FlagName)
// default to next sequence number if none provided
ctx, err = context.EnsureSequence(ctx)
if err != nil {
return err
}
// create the message
msg := cool.NewSetTrendMsg(from, args[0])
msg := cool.NewMsgSetTrend(from, args[0])
// build and sign the transaction, then broadcast to Tendermint
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
res, err := ctx.EnsureSignBuildBroadcast(name, msg, cdc)
if err != nil {
return err
}

View File

@ -6,12 +6,15 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Cool errors reserve 400 ~ 499.
const (
DefaultCodespace sdk.CodespaceType = 6
// Cool module reserves error 400-499 lawl
CodeIncorrectCoolAnswer sdk.CodeType = 400
)
// ErrIncorrectCoolAnswer - Error returned upon an incorrect guess
func ErrIncorrectCoolAnswer(answer string) sdk.Error {
return sdk.NewError(CodeIncorrectCoolAnswer, fmt.Sprintf("Incorrect cool answer: %v", answer))
func ErrIncorrectCoolAnswer(codespace sdk.CodespaceType, answer string) sdk.Error {
return sdk.NewError(codespace, CodeIncorrectCoolAnswer, fmt.Sprintf("Incorrect cool answer: %v", answer))
}

View File

@ -21,10 +21,10 @@ import (
func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case SetTrendMsg:
return handleSetTrendMsg(ctx, k, msg)
case QuizMsg:
return handleQuizMsg(ctx, k, msg)
case MsgSetTrend:
return handleMsgSetTrend(ctx, k, msg)
case MsgQuiz:
return handleMsgQuiz(ctx, k, msg)
default:
errMsg := fmt.Sprintf("Unrecognized cool Msg type: %v", reflect.TypeOf(msg).Name())
return sdk.ErrUnknownRequest(errMsg).Result()
@ -32,19 +32,19 @@ func NewHandler(k Keeper) sdk.Handler {
}
}
// Handle QuizMsg This is the engine of your module
func handleSetTrendMsg(ctx sdk.Context, k Keeper, msg SetTrendMsg) sdk.Result {
// Handle MsgQuiz This is the engine of your module
func handleMsgSetTrend(ctx sdk.Context, k Keeper, msg MsgSetTrend) sdk.Result {
k.setTrend(ctx, msg.Cool)
return sdk.Result{}
}
// Handle QuizMsg This is the engine of your module
func handleQuizMsg(ctx sdk.Context, k Keeper, msg QuizMsg) sdk.Result {
// Handle MsgQuiz This is the engine of your module
func handleMsgQuiz(ctx sdk.Context, k Keeper, msg MsgQuiz) sdk.Result {
correct := k.CheckTrend(ctx, msg.CoolAnswer)
if !correct {
return ErrIncorrectCoolAnswer(msg.CoolAnswer).Result()
return ErrIncorrectCoolAnswer(k.codespace, msg.CoolAnswer).Result()
}
if ctx.IsCheckTx() {

View File

@ -7,14 +7,16 @@ import (
// Keeper - handlers sets/gets of custom variables for your module
type Keeper struct {
ck bank.CoinKeeper
ck bank.Keeper
storeKey sdk.StoreKey // The (unexposed) key used to access the store from the Context.
codespace sdk.CodespaceType
}
// NewKeeper - Returns the Keeper
func NewKeeper(key sdk.StoreKey, bankKeeper bank.CoinKeeper) Keeper {
return Keeper{bankKeeper, key}
func NewKeeper(key sdk.StoreKey, bankKeeper bank.Keeper, codespace sdk.CodespaceType) Keeper {
return Keeper{bankKeeper, key, codespace}
}
// Key to knowing the trend on the streets!
@ -42,7 +44,13 @@ func (k Keeper) CheckTrend(ctx sdk.Context, guessedTrend string) bool {
}
// InitGenesis - store the genesis trend
func (k Keeper) InitGenesis(ctx sdk.Context, data CoolGenesis) error {
func InitGenesis(ctx sdk.Context, k Keeper, data Genesis) error {
k.setTrend(ctx, data.Trend)
return nil
}
// WriteGenesis - output the genesis trend
func WriteGenesis(ctx sdk.Context, k Keeper) Genesis {
trend := k.GetTrend(ctx)
return Genesis{trend}
}

View File

@ -0,0 +1,50 @@
package cool
import (
"testing"
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
auth "github.com/cosmos/cosmos-sdk/x/auth"
bank "github.com/cosmos/cosmos-sdk/x/bank"
)
func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey) {
db := dbm.NewMemDB()
capKey := sdk.NewKVStoreKey("capkey")
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db)
ms.LoadLatestVersion()
return ms, capKey
}
func TestCoolKeeper(t *testing.T) {
ms, capKey := setupMultiStore()
cdc := wire.NewCodec()
auth.RegisterBaseAccount(cdc)
am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{})
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, nil)
ck := bank.NewKeeper(am)
keeper := NewKeeper(capKey, ck, DefaultCodespace)
err := InitGenesis(ctx, keeper, Genesis{"icy"})
assert.Nil(t, err)
genesis := WriteGenesis(ctx, keeper)
assert.Nil(t, err)
assert.Equal(t, genesis, Genesis{"icy"})
res := keeper.GetTrend(ctx)
assert.Equal(t, res, "icy")
keeper.setTrend(ctx, "fiery")
res = keeper.GetTrend(ctx)
assert.Equal(t, res, "fiery")
}

View File

@ -8,39 +8,38 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// A really cool msg type, these fields are can be entirely arbitrary and
// a really cool msg type, these fields are can be entirely arbitrary and
// custom to your message
type SetTrendMsg struct {
type MsgSetTrend struct {
Sender sdk.Address
Cool string
}
// Genesis state - specify genesis trend
type CoolGenesis struct {
// genesis state - specify genesis trend
type Genesis struct {
Trend string `json:"trend"`
}
// New cool message
func NewSetTrendMsg(sender sdk.Address, cool string) SetTrendMsg {
return SetTrendMsg{
// new cool message
func NewMsgSetTrend(sender sdk.Address, cool string) MsgSetTrend {
return MsgSetTrend{
Sender: sender,
Cool: cool,
}
}
// enforce the msg type at compile time
var _ sdk.Msg = SetTrendMsg{}
var _ sdk.Msg = MsgSetTrend{}
// nolint
func (msg SetTrendMsg) Type() string { return "cool" }
func (msg SetTrendMsg) Get(key interface{}) (value interface{}) { return nil }
func (msg SetTrendMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
func (msg SetTrendMsg) String() string {
return fmt.Sprintf("SetTrendMsg{Sender: %v, Cool: %v}", msg.Sender, msg.Cool)
func (msg MsgSetTrend) Type() string { return "cool" }
func (msg MsgSetTrend) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
func (msg MsgSetTrend) String() string {
return fmt.Sprintf("MsgSetTrend{Sender: %v, Cool: %v}", msg.Sender, msg.Cool)
}
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
func (msg SetTrendMsg) ValidateBasic() sdk.Error {
func (msg MsgSetTrend) ValidateBasic() sdk.Error {
if len(msg.Sender) == 0 {
return sdk.ErrUnknownAddress(msg.Sender.String()).Trace("")
}
@ -54,7 +53,7 @@ func (msg SetTrendMsg) ValidateBasic() sdk.Error {
}
// Get the bytes for the message signer to sign on
func (msg SetTrendMsg) GetSignBytes() []byte {
func (msg MsgSetTrend) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)
@ -66,32 +65,31 @@ func (msg SetTrendMsg) GetSignBytes() []byte {
// A message type to quiz how cool you are. these fields are can be entirely
// arbitrary and custom to your message
type QuizMsg struct {
type MsgQuiz struct {
Sender sdk.Address
CoolAnswer string
}
// New cool message
func NewQuizMsg(sender sdk.Address, coolerthancool string) QuizMsg {
return QuizMsg{
func NewMsgQuiz(sender sdk.Address, coolerthancool string) MsgQuiz {
return MsgQuiz{
Sender: sender,
CoolAnswer: coolerthancool,
}
}
// enforce the msg type at compile time
var _ sdk.Msg = QuizMsg{}
var _ sdk.Msg = MsgQuiz{}
// nolint
func (msg QuizMsg) Type() string { return "cool" }
func (msg QuizMsg) Get(key interface{}) (value interface{}) { return nil }
func (msg QuizMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
func (msg QuizMsg) String() string {
return fmt.Sprintf("QuizMsg{Sender: %v, CoolAnswer: %v}", msg.Sender, msg.CoolAnswer)
func (msg MsgQuiz) Type() string { return "cool" }
func (msg MsgQuiz) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
func (msg MsgQuiz) String() string {
return fmt.Sprintf("MsgQuiz{Sender: %v, CoolAnswer: %v}", msg.Sender, msg.CoolAnswer)
}
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
func (msg QuizMsg) ValidateBasic() sdk.Error {
func (msg MsgQuiz) ValidateBasic() sdk.Error {
if len(msg.Sender) == 0 {
return sdk.ErrUnknownAddress(msg.Sender.String()).Trace("")
}
@ -99,7 +97,7 @@ func (msg QuizMsg) ValidateBasic() sdk.Error {
}
// Get the bytes for the message signer to sign on
func (msg QuizMsg) GetSignBytes() []byte {
func (msg MsgQuiz) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)

View File

@ -0,0 +1,11 @@
package cool
import (
"github.com/cosmos/cosmos-sdk/wire"
)
// Register concrete types on wire codec
func RegisterWire(cdc *wire.Codec) {
cdc.RegisterConcrete(MsgQuiz{}, "cool/Quiz", nil)
cdc.RegisterConcrete(MsgSetTrend{}, "cool/SetTrend", nil)
}

View File

@ -1,30 +1,25 @@
package commands
package cli
import (
"fmt"
"strconv"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
"github.com/cosmos/cosmos-sdk/wire"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
)
// command to mine some pow!
func MineCmd(cdc *wire.Codec) *cobra.Command {
return &cobra.Command{
Use: "mine [difficulty] [count] [nonce] [solution]",
Short: "Mine some coins with proof-of-work!",
Args: cobra.ExactArgs(4),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 4 {
return errors.New("You must provide a difficulty, a count, a solution, and a nonce (in that order)")
}
// get from address and parse arguments
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
from, err := ctx.GetFromAddress()
@ -36,12 +31,10 @@ func MineCmd(cdc *wire.Codec) *cobra.Command {
if err != nil {
return err
}
count, err := strconv.ParseUint(args[1], 0, 64)
if err != nil {
return err
}
nonce, err := strconv.ParseUint(args[2], 0, 64)
if err != nil {
return err
@ -49,19 +42,13 @@ func MineCmd(cdc *wire.Codec) *cobra.Command {
solution := []byte(args[3])
msg := pow.NewMineMsg(from, difficulty, count, nonce, solution)
msg := pow.NewMsgMine(from, difficulty, count, nonce, solution)
// get account name
name := ctx.FromAddressName
// default to next sequence number if none provided
ctx, err = context.EnsureSequence(ctx)
if err != nil {
return err
}
// build and sign the transaction, then broadcast to Tendermint
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
res, err := ctx.EnsureSignBuildBroadcast(name, msg, cdc)
if err != nil {
return err
}

View File

@ -4,17 +4,20 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// TODO remove, seems hacky
type CodeType = sdk.CodeType
// POW errors reserve 200 ~ 299
const (
CodeInvalidDifficulty CodeType = 201
CodeNonexistentDifficulty CodeType = 202
CodeNonexistentReward CodeType = 203
CodeNonexistentCount CodeType = 204
CodeInvalidProof CodeType = 205
CodeNotBelowTarget CodeType = 206
CodeInvalidCount CodeType = 207
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
DefaultCodespace sdk.CodespaceType = 5
CodeInvalidDifficulty CodeType = 201
CodeNonexistentDifficulty CodeType = 202
CodeNonexistentReward CodeType = 203
CodeNonexistentCount CodeType = 204
CodeInvalidProof CodeType = 205
CodeNotBelowTarget CodeType = 206
CodeInvalidCount CodeType = 207
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
)
func codeToDefaultMsg(code CodeType) string {
@ -40,43 +43,37 @@ func codeToDefaultMsg(code CodeType) string {
}
}
func ErrInvalidDifficulty(msg string) sdk.Error {
return newError(CodeInvalidDifficulty, msg)
// nolint
func ErrInvalidDifficulty(codespace sdk.CodespaceType, msg string) sdk.Error {
return newError(codespace, CodeInvalidDifficulty, msg)
}
func ErrNonexistentDifficulty() sdk.Error {
return newError(CodeNonexistentDifficulty, "")
func ErrNonexistentDifficulty(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeNonexistentDifficulty, "")
}
func ErrNonexistentReward() sdk.Error {
return newError(CodeNonexistentReward, "")
func ErrNonexistentReward(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeNonexistentReward, "")
}
func ErrNonexistentCount() sdk.Error {
return newError(CodeNonexistentCount, "")
func ErrNonexistentCount(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeNonexistentCount, "")
}
func ErrInvalidProof(msg string) sdk.Error {
return newError(CodeInvalidProof, msg)
func ErrInvalidProof(codespace sdk.CodespaceType, msg string) sdk.Error {
return newError(codespace, CodeInvalidProof, msg)
}
func ErrNotBelowTarget(msg string) sdk.Error {
return newError(CodeNotBelowTarget, msg)
func ErrNotBelowTarget(codespace sdk.CodespaceType, msg string) sdk.Error {
return newError(codespace, CodeNotBelowTarget, msg)
}
func ErrInvalidCount(msg string) sdk.Error {
return newError(CodeInvalidCount, msg)
func ErrInvalidCount(codespace sdk.CodespaceType, msg string) sdk.Error {
return newError(codespace, CodeInvalidCount, msg)
}
func msgOrDefaultMsg(msg string, code CodeType) string {
if msg != "" {
return msg
} else {
return codeToDefaultMsg(code)
}
return codeToDefaultMsg(code)
}
func newError(code CodeType, msg string) sdk.Error {
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
msg = msgOrDefaultMsg(msg, code)
return sdk.NewError(code, msg)
return sdk.NewError(codespace, code, msg)
}

View File

@ -6,17 +6,18 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// POW handler
func (pk Keeper) Handler(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case MineMsg:
return handleMineMsg(ctx, pk, msg)
case MsgMine:
return handleMsgMine(ctx, pk, msg)
default:
errMsg := "Unrecognized pow Msg type: " + reflect.TypeOf(msg).Name()
return sdk.ErrUnknownRequest(errMsg).Result()
}
}
func handleMineMsg(ctx sdk.Context, pk Keeper, msg MineMsg) sdk.Result {
func handleMsgMine(ctx sdk.Context, pk Keeper, msg MsgMine) sdk.Result {
// precondition: msg has passed ValidateBasic

View File

@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk/types"
wire "github.com/cosmos/cosmos-sdk/wire"
@ -19,10 +20,10 @@ func TestPowHandler(t *testing.T) {
auth.RegisterBaseAccount(cdc)
am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{})
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
config := NewPowConfig("pow", int64(1))
ck := bank.NewCoinKeeper(am)
keeper := NewKeeper(capKey, config, ck)
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
config := NewConfig("pow", int64(1))
ck := bank.NewKeeper(am)
keeper := NewKeeper(capKey, config, ck, DefaultCodespace)
handler := keeper.Handler
@ -30,11 +31,11 @@ func TestPowHandler(t *testing.T) {
count := uint64(1)
difficulty := uint64(2)
err := keeper.InitGenesis(ctx, PowGenesis{uint64(1), uint64(0)})
err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)})
assert.Nil(t, err)
nonce, proof := mine(addr, count, difficulty)
msg := NewMineMsg(addr, difficulty, count, nonce, proof)
msg := NewMsgMine(addr, difficulty, count, nonce, proof)
result := handler(ctx, msg)
assert.Equal(t, result, sdk.Result{})
@ -51,7 +52,7 @@ func TestPowHandler(t *testing.T) {
difficulty = uint64(4)
nonce, proof = mine(addr, count, difficulty)
msg = NewMineMsg(addr, difficulty, count, nonce, proof)
msg = NewMsgMine(addr, difficulty, count, nonce, proof)
result = handler(ctx, msg)
assert.NotEqual(t, result, sdk.Result{})

View File

@ -9,41 +9,61 @@ import (
)
// module users must specify coin denomination and reward (constant) per PoW solution
type PowConfig struct {
type Config struct {
Denomination string
Reward int64
}
// genesis info must specify starting difficulty and starting count
type PowGenesis struct {
type Genesis struct {
Difficulty uint64 `json:"difficulty"`
Count uint64 `json:"count"`
}
// POW Keeper
type Keeper struct {
key sdk.StoreKey
config PowConfig
ck bank.CoinKeeper
key sdk.StoreKey
config Config
ck bank.Keeper
codespace sdk.CodespaceType
}
func NewPowConfig(denomination string, reward int64) PowConfig {
return PowConfig{denomination, reward}
func NewConfig(denomination string, reward int64) Config {
return Config{denomination, reward}
}
func NewKeeper(key sdk.StoreKey, config PowConfig, ck bank.CoinKeeper) Keeper {
return Keeper{key, config, ck}
func NewKeeper(key sdk.StoreKey, config Config, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
return Keeper{key, config, ck, codespace}
}
func (pk Keeper) InitGenesis(ctx sdk.Context, genesis PowGenesis) error {
pk.SetLastDifficulty(ctx, genesis.Difficulty)
pk.SetLastCount(ctx, genesis.Count)
// InitGenesis for the POW module
func InitGenesis(ctx sdk.Context, k Keeper, genesis Genesis) error {
k.SetLastDifficulty(ctx, genesis.Difficulty)
k.SetLastCount(ctx, genesis.Count)
return nil
}
// WriteGenesis for the PoW module
func WriteGenesis(ctx sdk.Context, k Keeper) Genesis {
difficulty, err := k.GetLastDifficulty(ctx)
if err != nil {
panic(err)
}
count, err := k.GetLastCount(ctx)
if err != nil {
panic(err)
}
return Genesis{
difficulty,
count,
}
}
var lastDifficultyKey = []byte("lastDifficultyKey")
func (pk Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
store := ctx.KVStore(pk.key)
// get the last mining difficulty
func (k Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
store := ctx.KVStore(k.key)
stored := store.Get(lastDifficultyKey)
if stored == nil {
panic("no stored difficulty")
@ -52,15 +72,17 @@ func (pk Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
}
}
func (pk Keeper) SetLastDifficulty(ctx sdk.Context, diff uint64) {
store := ctx.KVStore(pk.key)
// set the last mining difficulty
func (k Keeper) SetLastDifficulty(ctx sdk.Context, diff uint64) {
store := ctx.KVStore(k.key)
store.Set(lastDifficultyKey, []byte(strconv.FormatUint(diff, 16)))
}
var countKey = []byte("count")
func (pk Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
store := ctx.KVStore(pk.key)
// get the last count
func (k Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
store := ctx.KVStore(k.key)
stored := store.Get(countKey)
if stored == nil {
panic("no stored count")
@ -69,45 +91,45 @@ func (pk Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
}
}
func (pk Keeper) SetLastCount(ctx sdk.Context, count uint64) {
store := ctx.KVStore(pk.key)
// set the last count
func (k Keeper) SetLastCount(ctx sdk.Context, count uint64) {
store := ctx.KVStore(k.key)
store.Set(countKey, []byte(strconv.FormatUint(count, 16)))
}
func (pk Keeper) CheckValid(ctx sdk.Context, difficulty uint64, count uint64) (uint64, uint64, sdk.Error) {
// Is the keeper state valid?
func (k Keeper) CheckValid(ctx sdk.Context, difficulty uint64, count uint64) (uint64, uint64, sdk.Error) {
lastDifficulty, err := pk.GetLastDifficulty(ctx)
lastDifficulty, err := k.GetLastDifficulty(ctx)
if err != nil {
return 0, 0, ErrNonexistentDifficulty()
return 0, 0, ErrNonexistentDifficulty(k.codespace)
}
newDifficulty := lastDifficulty + 1
lastCount, err := pk.GetLastCount(ctx)
lastCount, err := k.GetLastCount(ctx)
if err != nil {
return 0, 0, ErrNonexistentCount()
return 0, 0, ErrNonexistentCount(k.codespace)
}
newCount := lastCount + 1
if count != newCount {
return 0, 0, ErrInvalidCount(fmt.Sprintf("invalid count: was %d, should have been %d", count, newCount))
return 0, 0, ErrInvalidCount(k.codespace, fmt.Sprintf("invalid count: was %d, should have been %d", count, newCount))
}
if difficulty != newDifficulty {
return 0, 0, ErrInvalidDifficulty(fmt.Sprintf("invalid difficulty: was %d, should have been %d", difficulty, newDifficulty))
return 0, 0, ErrInvalidDifficulty(k.codespace, fmt.Sprintf("invalid difficulty: was %d, should have been %d", difficulty, newDifficulty))
}
return newDifficulty, newCount, nil
}
func (pk Keeper) ApplyValid(ctx sdk.Context, sender sdk.Address, newDifficulty uint64, newCount uint64) sdk.Error {
_, ckErr := pk.ck.AddCoins(ctx, sender, []sdk.Coin{sdk.Coin{pk.config.Denomination, pk.config.Reward}})
// Add some coins for a POW well done
func (k Keeper) ApplyValid(ctx sdk.Context, sender sdk.Address, newDifficulty uint64, newCount uint64) sdk.Error {
_, ckErr := k.ck.AddCoins(ctx, sender, []sdk.Coin{sdk.Coin{k.config.Denomination, k.config.Reward}})
if ckErr != nil {
return ckErr
}
pk.SetLastDifficulty(ctx, newDifficulty)
pk.SetLastCount(ctx, newCount)
k.SetLastDifficulty(ctx, newDifficulty)
k.SetLastCount(ctx, newCount)
return nil
}

View File

@ -7,6 +7,7 @@ import (
abci "github.com/tendermint/abci/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -32,14 +33,18 @@ func TestPowKeeperGetSet(t *testing.T) {
auth.RegisterBaseAccount(cdc)
am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{})
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
config := NewPowConfig("pow", int64(1))
ck := bank.NewCoinKeeper(am)
keeper := NewKeeper(capKey, config, ck)
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
config := NewConfig("pow", int64(1))
ck := bank.NewKeeper(am)
keeper := NewKeeper(capKey, config, ck, DefaultCodespace)
err := keeper.InitGenesis(ctx, PowGenesis{uint64(1), uint64(0)})
err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)})
assert.Nil(t, err)
genesis := WriteGenesis(ctx, keeper)
assert.Nil(t, err)
assert.Equal(t, genesis, Genesis{uint64(1), uint64(0)})
res, err := keeper.GetLastDifficulty(ctx)
assert.Nil(t, err)
assert.Equal(t, res, uint64(1))

View File

@ -9,9 +9,10 @@ import (
crypto "github.com/tendermint/go-crypto"
)
func GenerateMineMsg(sender sdk.Address, count uint64, difficulty uint64) MineMsg {
// generate the mine message
func GenerateMsgMine(sender sdk.Address, count uint64, difficulty uint64) MsgMine {
nonce, hash := mine(sender, count, difficulty)
return NewMineMsg(sender, difficulty, count, nonce, hash)
return NewMsgMine(sender, difficulty, count, nonce, hash)
}
func hash(sender sdk.Address, count uint64, nonce uint64) []byte {

View File

@ -13,8 +13,8 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// MineMsg - mine some coins with PoW
type MineMsg struct {
// MsgMine - mine some coins with PoW
type MsgMine struct {
Sender sdk.Address `json:"sender"`
Difficulty uint64 `json:"difficulty"`
Count uint64 `json:"count"`
@ -23,21 +23,22 @@ type MineMsg struct {
}
// enforce the msg type at compile time
var _ sdk.Msg = MineMsg{}
var _ sdk.Msg = MsgMine{}
// NewMineMsg - construct mine message
func NewMineMsg(sender sdk.Address, difficulty uint64, count uint64, nonce uint64, proof []byte) MineMsg {
return MineMsg{sender, difficulty, count, nonce, proof}
// NewMsgMine - construct mine message
func NewMsgMine(sender sdk.Address, difficulty uint64, count uint64, nonce uint64, proof []byte) MsgMine {
return MsgMine{sender, difficulty, count, nonce, proof}
}
func (msg MineMsg) Type() string { return "pow" }
func (msg MineMsg) Get(key interface{}) (value interface{}) { return nil }
func (msg MineMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
func (msg MineMsg) String() string {
return fmt.Sprintf("MineMsg{Sender: %v, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
// nolint
func (msg MsgMine) Type() string { return "pow" }
func (msg MsgMine) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
func (msg MsgMine) String() string {
return fmt.Sprintf("MsgMine{Sender: %v, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
}
func (msg MineMsg) ValidateBasic() sdk.Error {
// validate the mine message
func (msg MsgMine) ValidateBasic() sdk.Error {
// check hash
var data []byte
// hash must include sender, so no other users can race the tx
@ -52,7 +53,7 @@ func (msg MineMsg) ValidateBasic() sdk.Error {
hex.Encode(hashHex, hash)
hashHex = hashHex[:16]
if !bytes.Equal(hashHex, msg.Proof) {
return ErrInvalidProof(fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
}
// check proof below difficulty
@ -60,16 +61,17 @@ func (msg MineMsg) ValidateBasic() sdk.Error {
target := math.MaxUint64 / msg.Difficulty
hashUint, err := strconv.ParseUint(string(msg.Proof), 16, 64)
if err != nil {
return ErrInvalidProof(fmt.Sprintf("proof: %s", msg.Proof))
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("proof: %s", msg.Proof))
}
if hashUint >= target {
return ErrNotBelowTarget(fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
return ErrNotBelowTarget(DefaultCodespace, fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
}
return nil
}
func (msg MineMsg) GetSignBytes() []byte {
// get the mine message sign bytes
func (msg MsgMine) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)

View File

@ -9,70 +9,65 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestNewMineMsg(t *testing.T) {
func TestNewMsgMine(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 0, 0, 0, []byte("")}
equiv := NewMineMsg(addr, 0, 0, 0, []byte(""))
msg := MsgMine{addr, 0, 0, 0, []byte("")}
equiv := NewMsgMine(addr, 0, 0, 0, []byte(""))
assert.Equal(t, msg, equiv, "%s != %s", msg, equiv)
}
func TestMineMsgType(t *testing.T) {
func TestMsgMineType(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 0, 0, 0, []byte("")}
msg := MsgMine{addr, 0, 0, 0, []byte("")}
assert.Equal(t, msg.Type(), "pow")
}
func TestMineMsgValidation(t *testing.T) {
func TestMsgMineValidation(t *testing.T) {
addr := sdk.Address([]byte("sender"))
otherAddr := sdk.Address([]byte("another"))
count := uint64(0)
for difficulty := uint64(1); difficulty < 1000; difficulty += 100 {
count += 1
count++
nonce, proof := mine(addr, count, difficulty)
msg := MineMsg{addr, difficulty, count, nonce, proof}
msg := MsgMine{addr, difficulty, count, nonce, proof}
err := msg.ValidateBasic()
assert.Nil(t, err, "error with difficulty %d - %+v", difficulty, err)
msg.Count += 1
msg.Count++
err = msg.ValidateBasic()
assert.NotNil(t, err, "count was wrong, should have thrown error with msg %s", msg)
msg.Count -= 1
msg.Nonce += 1
msg.Count--
msg.Nonce++
err = msg.ValidateBasic()
assert.NotNil(t, err, "nonce was wrong, should have thrown error with msg %s", msg)
msg.Nonce -= 1
msg.Nonce--
msg.Sender = otherAddr
err = msg.ValidateBasic()
assert.NotNil(t, err, "sender was wrong, should have thrown error with msg %s", msg)
}
}
func TestMineMsgString(t *testing.T) {
func TestMsgMineString(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 0, 0, 0, []byte("abc")}
msg := MsgMine{addr, 0, 0, 0, []byte("abc")}
res := msg.String()
assert.Equal(t, res, "MineMsg{Sender: 73656E646572, Difficulty: 0, Count: 0, Nonce: 0, Proof: abc}")
assert.Equal(t, res, "MsgMine{Sender: 73656E646572, Difficulty: 0, Count: 0, Nonce: 0, Proof: abc}")
}
func TestMineMsgGet(t *testing.T) {
func TestMsgMineGetSignBytes(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 0, 0, 0, []byte("")}
res := msg.Get(nil)
assert.Nil(t, res)
}
func TestMineMsgGetSignBytes(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 1, 1, 1, []byte("abc")}
msg := MsgMine{addr, 1, 1, 1, []byte("abc")}
res := msg.GetSignBytes()
assert.Equal(t, string(res), `{"sender":"73656E646572","difficulty":1,"count":1,"nonce":1,"proof":"YWJj"}`)
}
func TestMineMsgGetSigners(t *testing.T) {
func TestMsgMineGetSigners(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 1, 1, 1, []byte("abc")}
msg := MsgMine{addr, 1, 1, 1, []byte("abc")}
res := msg.GetSigners()
assert.Equal(t, fmt.Sprintf("%v", res), "[73656E646572]")
}

View File

@ -0,0 +1,10 @@
package pow
import (
"github.com/cosmos/cosmos-sdk/wire"
)
// Register concrete types on wire codec
func RegisterWire(cdc *wire.Codec) {
cdc.RegisterConcrete(MsgMine{}, "pow/Mine", nil)
}

View File

@ -0,0 +1,97 @@
package cli
import (
"encoding/hex"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
crypto "github.com/tendermint/go-crypto"
"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/examples/democoin/x/simplestake"
)
const (
flagStake = "stake"
flagValidator = "validator"
)
// simple bond tx
func BondTxCmd(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "bond",
Short: "Bond to a validator",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.NewCoreContextFromViper()
from, err := ctx.GetFromAddress()
if err != nil {
return err
}
stakeString := viper.GetString(flagStake)
if len(stakeString) == 0 {
return fmt.Errorf("specify coins to bond with --stake")
}
valString := viper.GetString(flagValidator)
if len(valString) == 0 {
return fmt.Errorf("specify pubkey to bond to with --validator")
}
stake, err := sdk.ParseCoin(stakeString)
if err != nil {
return err
}
// TODO: bech32 ...
rawPubKey, err := hex.DecodeString(valString)
if err != nil {
return err
}
var pubKeyEd crypto.PubKeyEd25519
copy(pubKeyEd[:], rawPubKey)
msg := simplestake.NewMsgBond(from, stake, pubKeyEd)
return sendMsg(cdc, msg)
},
}
cmd.Flags().String(flagStake, "", "Amount of coins to stake")
cmd.Flags().String(flagValidator, "", "Validator address to stake")
return cmd
}
// simple unbond tx
func UnbondTxCmd(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "unbond",
Short: "Unbond from a validator",
RunE: func(cmd *cobra.Command, args []string) error {
from, err := context.NewCoreContextFromViper().GetFromAddress()
if err != nil {
return err
}
msg := simplestake.NewMsgUnbond(from)
return sendMsg(cdc, msg)
},
}
return cmd
}
func sendMsg(cdc *wire.Codec, msg sdk.Msg) error {
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
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
}

View File

@ -0,0 +1,37 @@
package simplestake
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// simple stake errors reserve 300 ~ 399.
const (
DefaultCodespace sdk.CodespaceType = 4
// simplestake errors reserve 300 - 399.
CodeEmptyValidator sdk.CodeType = 300
CodeInvalidUnbond sdk.CodeType = 301
CodeEmptyStake sdk.CodeType = 302
CodeIncorrectStakingToken sdk.CodeType = 303
)
// nolint
func ErrIncorrectStakingToken(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeIncorrectStakingToken, "")
}
func ErrEmptyValidator(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeEmptyValidator, "")
}
func ErrInvalidUnbond(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeInvalidUnbond, "")
}
func ErrEmptyStake(codespace sdk.CodespaceType) sdk.Error {
return newError(codespace, CodeEmptyStake, "")
}
// -----------------------------
// Helpers
func newError(codespace sdk.CodespaceType, code sdk.CodeType, msg string) sdk.Error {
return sdk.NewError(codespace, code, msg)
}

View File

@ -10,17 +10,17 @@ import (
func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case BondMsg:
return handleBondMsg(ctx, k, msg)
case UnbondMsg:
return handleUnbondMsg(ctx, k, msg)
case MsgBond:
return handleMsgBond(ctx, k, msg)
case MsgUnbond:
return handleMsgUnbond(ctx, k, msg)
default:
return sdk.ErrUnknownRequest("No match for message type.").Result()
}
}
}
func handleBondMsg(ctx sdk.Context, k Keeper, msg BondMsg) sdk.Result {
func handleMsgBond(ctx sdk.Context, k Keeper, msg MsgBond) sdk.Result {
power, err := k.Bond(ctx, msg.Address, msg.PubKey, msg.Stake)
if err != nil {
return err.Result()
@ -32,12 +32,12 @@ func handleBondMsg(ctx sdk.Context, k Keeper, msg BondMsg) sdk.Result {
}
return sdk.Result{
Code: sdk.CodeOK,
Code: sdk.ABCICodeOK,
ValidatorUpdates: abci.Validators{valSet},
}
}
func handleUnbondMsg(ctx sdk.Context, k Keeper, msg UnbondMsg) sdk.Result {
func handleMsgUnbond(ctx sdk.Context, k Keeper, msg MsgUnbond) sdk.Result {
pubKey, _, err := k.Unbond(ctx, msg.Address)
if err != nil {
return err.Result()
@ -49,7 +49,7 @@ func handleUnbondMsg(ctx sdk.Context, k Keeper, msg UnbondMsg) sdk.Result {
}
return sdk.Result{
Code: sdk.CodeOK,
Code: sdk.ABCICodeOK,
ValidatorUpdates: abci.Validators{valSet},
}
}

View File

@ -12,20 +12,23 @@ const stakingToken = "steak"
const moduleName = "simplestake"
// simple stake keeper
type Keeper struct {
ck bank.CoinKeeper
ck bank.Keeper
key sdk.StoreKey
cdc *wire.Codec
key sdk.StoreKey
cdc *wire.Codec
codespace sdk.CodespaceType
}
func NewKeeper(key sdk.StoreKey, coinKeeper bank.CoinKeeper) Keeper {
func NewKeeper(key sdk.StoreKey, coinKeeper bank.Keeper, codespace sdk.CodespaceType) Keeper {
cdc := wire.NewCodec()
wire.RegisterCrypto(cdc)
return Keeper{
key: key,
cdc: cdc,
ck: coinKeeper,
key: key,
cdc: cdc,
ck: coinKeeper,
codespace: codespace,
}
}
@ -57,9 +60,10 @@ func (k Keeper) deleteBondInfo(ctx sdk.Context, addr sdk.Address) {
store.Delete(addr)
}
// register a bond with the keeper
func (k Keeper) Bond(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, stake sdk.Coin) (int64, sdk.Error) {
if stake.Denom != stakingToken {
return 0, ErrIncorrectStakingToken()
return 0, ErrIncorrectStakingToken(k.codespace)
}
_, err := k.ck.SubtractCoins(ctx, addr, []sdk.Coin{stake})
@ -81,10 +85,11 @@ func (k Keeper) Bond(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, st
return bi.Power, nil
}
// register an unbond with the keeper
func (k Keeper) Unbond(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64, sdk.Error) {
bi := k.getBondInfo(ctx, addr)
if bi.isEmpty() {
return nil, 0, ErrInvalidUnbond()
return nil, 0, ErrInvalidUnbond(k.codespace)
}
k.deleteBondInfo(ctx, addr)
@ -102,7 +107,7 @@ func (k Keeper) Unbond(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64,
func (k Keeper) bondWithoutCoins(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, stake sdk.Coin) (int64, sdk.Error) {
if stake.Denom != stakingToken {
return 0, ErrIncorrectStakingToken()
return 0, ErrIncorrectStakingToken(k.codespace)
}
bi := k.getBondInfo(ctx, addr)
@ -122,7 +127,7 @@ func (k Keeper) bondWithoutCoins(ctx sdk.Context, addr sdk.Address, pubKey crypt
func (k Keeper) unbondWithoutCoins(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64, sdk.Error) {
bi := k.getBondInfo(ctx, addr)
if bi.isEmpty() {
return nil, 0, ErrInvalidUnbond()
return nil, 0, ErrInvalidUnbond(k.codespace)
}
k.deleteBondInfo(ctx, addr)

View File

@ -10,6 +10,7 @@ import (
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"
@ -32,8 +33,8 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) {
func TestKeeperGetSet(t *testing.T) {
ms, _, capKey := setupMultiStore()
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
stakeKeeper := NewKeeper(capKey, bank.NewCoinKeeper(nil))
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
stakeKeeper := NewKeeper(capKey, bank.NewKeeper(nil), DefaultCodespace)
addr := sdk.Address([]byte("some-address"))
bi := stakeKeeper.getBondInfo(ctx, addr)
@ -59,17 +60,17 @@ func TestBonding(t *testing.T) {
cdc := wire.NewCodec()
auth.RegisterBaseAccount(cdc)
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{})
coinKeeper := bank.NewCoinKeeper(accountMapper)
stakeKeeper := NewKeeper(capKey, coinKeeper)
coinKeeper := bank.NewKeeper(accountMapper)
stakeKeeper := NewKeeper(capKey, coinKeeper, DefaultCodespace)
addr := sdk.Address([]byte("some-address"))
privKey := crypto.GenPrivKeyEd25519()
pubKey := privKey.PubKey()
_, _, err := stakeKeeper.unbondWithoutCoins(ctx, addr)
assert.Equal(t, err, ErrInvalidUnbond())
assert.Equal(t, err, ErrInvalidUnbond(DefaultCodespace))
_, err = stakeKeeper.bondWithoutCoins(ctx, addr, pubKey, sdk.Coin{"steak", 10})
assert.Nil(t, err)
@ -82,5 +83,5 @@ func TestBonding(t *testing.T) {
assert.Equal(t, pubKey, pk)
_, _, err = stakeKeeper.unbondWithoutCoins(ctx, addr)
assert.Equal(t, err, ErrInvalidUnbond())
assert.Equal(t, err, ErrInvalidUnbond(DefaultCodespace))
}

View File

@ -0,0 +1,79 @@
package simplestake
import (
"encoding/json"
crypto "github.com/tendermint/go-crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
)
//_________________________________________________________----
// simple bond message
type MsgBond struct {
Address sdk.Address `json:"address"`
Stake sdk.Coin `json:"coins"`
PubKey crypto.PubKey `json:"pub_key"`
}
func NewMsgBond(addr sdk.Address, stake sdk.Coin, pubKey crypto.PubKey) MsgBond {
return MsgBond{
Address: addr,
Stake: stake,
PubKey: pubKey,
}
}
//nolint
func (msg MsgBond) Type() string { return moduleName } //TODO update "stake/declarecandidacy"
func (msg MsgBond) GetSigners() []sdk.Address { return []sdk.Address{msg.Address} }
// basic validation of the bond message
func (msg MsgBond) ValidateBasic() sdk.Error {
if msg.Stake.IsZero() {
return ErrEmptyStake(DefaultCodespace)
}
if msg.PubKey == nil {
return sdk.ErrInvalidPubKey("MsgBond.PubKey must not be empty")
}
return nil
}
// get bond message sign bytes
func (msg MsgBond) GetSignBytes() []byte {
bz, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return bz
}
//_______________________________________________________________
// simple unbond message
type MsgUnbond struct {
Address sdk.Address `json:"address"`
}
func NewMsgUnbond(addr sdk.Address) MsgUnbond {
return MsgUnbond{
Address: addr,
}
}
//nolint
func (msg MsgUnbond) Type() string { return moduleName } //TODO update "stake/declarecandidacy"
func (msg MsgUnbond) GetSigners() []sdk.Address { return []sdk.Address{msg.Address} }
func (msg MsgUnbond) ValidateBasic() sdk.Error { return nil }
// get unbond message sign bytes
func (msg MsgUnbond) GetSignBytes() []byte {
bz, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return bz
}

View File

@ -14,14 +14,14 @@ func TestBondMsgValidation(t *testing.T) {
privKey := crypto.GenPrivKeyEd25519()
cases := []struct {
valid bool
bondMsg BondMsg
msgBond MsgBond
}{
{true, NewBondMsg(sdk.Address{}, sdk.Coin{"mycoin", 5}, privKey.PubKey())},
{false, NewBondMsg(sdk.Address{}, sdk.Coin{"mycoin", 0}, privKey.PubKey())},
{true, NewMsgBond(sdk.Address{}, sdk.Coin{"mycoin", 5}, privKey.PubKey())},
{false, NewMsgBond(sdk.Address{}, sdk.Coin{"mycoin", 0}, privKey.PubKey())},
}
for i, tc := range cases {
err := tc.bondMsg.ValidateBasic()
err := tc.msgBond.ValidateBasic()
if tc.valid {
assert.Nil(t, err, "%d: %+v", i, err)
} else {

View File

@ -0,0 +1,11 @@
package simplestake
import (
"github.com/cosmos/cosmos-sdk/wire"
)
// Register concrete types on wire codec
func RegisterWire(cdc *wire.Codec) {
cdc.RegisterConcrete(MsgBond{}, "simplestake/BondMsg", nil)
cdc.RegisterConcrete(MsgUnbond{}, "simplestake/UnbondMsg", nil)
}

View File

@ -32,7 +32,7 @@ func main() {
var capKeyMainStore = sdk.NewKVStoreKey("main")
// Create BaseApp.
var baseApp = bam.NewBaseApp("kvstore", logger, db)
var baseApp = bam.NewBaseApp("kvstore", nil, logger, db)
// Set mounts for BaseApp's MultiStore.
baseApp.MountStoresIAVL(capKeyMainStore)
@ -41,7 +41,7 @@ func main() {
baseApp.SetTxDecoder(decodeTx)
// Set a handler Route.
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore))
baseApp.Router().AddRoute("kvstore", Handler(capKeyMainStore))
// Load latest version.
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
@ -65,11 +65,12 @@ func main() {
return
}
func KVStoreHandler(storeKey sdk.StoreKey) sdk.Handler {
// KVStore Handler
func Handler(storeKey sdk.StoreKey) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
dTx, ok := msg.(kvstoreTx)
if !ok {
panic("KVStoreHandler should only receive kvstoreTx")
panic("Handler should only receive kvstoreTx")
}
// tx is already unmarshalled

View File

@ -13,19 +13,6 @@ type kvstoreTx struct {
bytes []byte
}
func (tx kvstoreTx) Get(key interface{}) (value interface{}) {
switch k := key.(type) {
case string:
switch k {
case "key":
return tx.key
case "value":
return tx.value
}
}
return nil
}
func (tx kvstoreTx) Type() string {
return "kvstore"
}

View File

@ -6,16 +6,18 @@ import (
"path/filepath"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
bam "github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
// NewApp creates a simple mock kvstore app for testing.
// It should work similar to a real app.
// Make sure rootDir is empty before running the test,
// NewApp creates a simple mock kvstore app for testing. It should work
// similar to a real app. Make sure rootDir is empty before running the test,
// in order to guarantee consistent results
func NewApp(rootDir string, logger log.Logger) (abci.Application, error) {
db, err := dbm.NewGoLevelDB("mock", filepath.Join(rootDir, "data"))
@ -27,7 +29,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) {
capKeyMainStore := sdk.NewKVStoreKey("main")
// Create BaseApp.
baseApp := bam.NewBaseApp("kvstore", logger, db)
baseApp := bam.NewBaseApp("kvstore", nil, logger, db)
// Set mounts for BaseApp's MultiStore.
baseApp.MountStoresIAVL(capKeyMainStore)
@ -103,11 +105,10 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci
}
}
// GenInitOptions can be passed into InitCmd,
// returns a static string of a few key-values that can be parsed
// by InitChainer
func GenInitOptions(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
opts := []byte(`{
// AppGenState can be passed into InitCmd, returns a static string of a few
// key-values that can be parsed by InitChainer
func AppGenState(_ *wire.Codec, _ []json.RawMessage) (appState json.RawMessage, err error) {
appState = json.RawMessage(`{
"values": [
{
"key": "hello",
@ -119,5 +120,16 @@ func GenInitOptions(args []string, addr sdk.Address, coinDenom string) (json.Raw
}
]
}`)
return opts, nil
return
}
// Return a validator, not much else
func AppGenTx(_ *wire.Codec, pk crypto.PubKey) (
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
validator = tmtypes.GenesisValidator{
PubKey: pk,
Power: 10,
}
return
}

View File

@ -21,9 +21,13 @@ func TestInitApp(t *testing.T) {
require.NoError(t, err)
// initialize it future-way
opts, err := GenInitOptions(nil, nil, "")
appState, err := AppGenState(nil, nil)
require.NoError(t, err)
req := abci.RequestInitChain{AppStateBytes: opts}
//TODO test validators in the init chain?
req := abci.RequestInitChain{
AppStateBytes: appState,
}
app.InitChain(req)
app.Commit()

112
mock/store.go Normal file
View File

@ -0,0 +1,112 @@
package mock
import (
dbm "github.com/tendermint/tmlibs/db"
sdk "github.com/cosmos/cosmos-sdk/types"
)
type multiStore struct {
kv map[sdk.StoreKey]kvStore
}
func (ms multiStore) CacheMultiStore() sdk.CacheMultiStore {
panic("not implemented")
}
func (ms multiStore) CacheWrap() sdk.CacheWrap {
panic("not implemented")
}
func (ms multiStore) Commit() sdk.CommitID {
panic("not implemented")
}
func (ms multiStore) LastCommitID() sdk.CommitID {
panic("not implemented")
}
func (ms multiStore) GetCommitKVStore(key sdk.StoreKey) sdk.CommitKVStore {
panic("not implemented")
}
func (ms multiStore) GetCommitStore(key sdk.StoreKey) sdk.CommitStore {
panic("not implemented")
}
func (ms multiStore) MountStoreWithDB(key sdk.StoreKey, typ sdk.StoreType, db dbm.DB) {
ms.kv[key] = kvStore{store: make(map[string][]byte)}
}
func (ms multiStore) LoadLatestVersion() error {
return nil
}
func (ms multiStore) LoadVersion(ver int64) error {
panic("not implemented")
}
func (ms multiStore) GetKVStore(key sdk.StoreKey) sdk.KVStore {
return ms.kv[key]
}
func (ms multiStore) GetStore(key sdk.StoreKey) sdk.Store {
panic("not implemented")
}
func (ms multiStore) GetStoreType() sdk.StoreType {
panic("not implemented")
}
type kvStore struct {
store map[string][]byte
}
func (kv kvStore) CacheWrap() sdk.CacheWrap {
panic("not implemented")
}
func (kv kvStore) GetStoreType() sdk.StoreType {
panic("not implemented")
}
func (kv kvStore) Get(key []byte) []byte {
v, ok := kv.store[string(key)]
if !ok {
return nil
}
return v
}
func (kv kvStore) Has(key []byte) bool {
_, ok := kv.store[string(key)]
return ok
}
func (kv kvStore) Set(key, value []byte) {
kv.store[string(key)] = value
}
func (kv kvStore) Delete(key []byte) {
delete(kv.store, string(key))
}
func (kv kvStore) Iterator(start, end []byte) sdk.Iterator {
panic("not implemented")
}
func (kv kvStore) ReverseIterator(start, end []byte) sdk.Iterator {
panic("not implemented")
}
func (kv kvStore) SubspaceIterator(prefix []byte) sdk.Iterator {
panic("not implemented")
}
func (kv kvStore) ReverseSubspaceIterator(prefix []byte) sdk.Iterator {
panic("not implemented")
}
func NewCommitMultiStore(db dbm.DB) sdk.CommitMultiStore {
return multiStore{kv: make(map[sdk.StoreKey]kvStore)}
}

33
mock/store_test.go Normal file
View File

@ -0,0 +1,33 @@
package mock
import (
"testing"
"github.com/stretchr/testify/assert"
dbm "github.com/tendermint/tmlibs/db"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestStore(t *testing.T) {
db := dbm.NewMemDB()
cms := NewCommitMultiStore(db)
key := sdk.NewKVStoreKey("test")
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
err := cms.LoadLatestVersion()
assert.Nil(t, err)
store := cms.GetKVStore(key)
assert.NotNil(t, store)
k := []byte("hello")
v := []byte("world")
assert.False(t, store.Has(k))
store.Set(k, v)
assert.True(t, store.Has(k))
assert.Equal(t, v, store.Get(k))
store.Delete(k)
assert.False(t, store.Has(k))
}

View File

@ -26,19 +26,6 @@ func NewTx(key, value string) kvstoreTx {
}
}
func (tx kvstoreTx) Get(key interface{}) (value interface{}) {
switch k := key.(type) {
case string:
switch k {
case "key":
return tx.key
case "value":
return tx.value
}
}
return nil
}
func (tx kvstoreTx) Type() string {
return "kvstore"
}

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