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: | command: |
export PATH="$GOBIN:$PATH" export PATH="$GOBIN:$PATH"
make get_vendor_deps 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: - run:
name: binaries name: binaries
command: | command: |
export PATH="$GOBIN:$PATH" export PATH="$GOBIN:$PATH"
make build make install
- persist_to_workspace: - persist_to_workspace:
root: /tmp/workspace root: /tmp/workspace
paths: paths:
@ -46,6 +52,54 @@ jobs:
paths: paths:
- /go/src/github.com/cosmos/cosmos-sdk - /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: test_cover:
<<: *defaults <<: *defaults
parallelism: 4 parallelism: 4
@ -59,6 +113,8 @@ jobs:
- run: - run:
name: Run tests name: Run tests
command: | 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 for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v /vendor/ | circleci tests split --split-by=timings); do
id=$(basename "$pkg") id=$(basename "$pkg")
@ -94,10 +150,18 @@ workflows:
test-suite: test-suite:
jobs: jobs:
- setup_dependencies - setup_dependencies
- lint:
requires:
- setup_dependencies
- test_unit:
requires:
- setup_dependencies
- test_cli:
requires:
- setup_dependencies
- test_cover: - test_cover:
requires: requires:
- setup_dependencies - setup_dependencies
- upload_coverage: - upload_coverage:
requires: requires:
- test_cover - test_cover

View File

@ -1,12 +1,50 @@
# Changelog # 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: FEATURES:
* Add CacheContext * Gaia stake commands include, DeclareCandidacy, EditCandidacy, Delegate, Unbond
* Add auto sequencing to client * MountStoreWithDB without providing a custom store works.
* Add FeeHandler to ante handler * 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 BREAKING CHANGES
@ -14,8 +52,13 @@ BREAKING CHANGES
* [store] Add `SubspaceIterator` and `ReverseSubspaceIterator` to `KVStore` interface * [store] Add `SubspaceIterator` and `ReverseSubspaceIterator` to `KVStore` interface
* [basecoin] NewBasecoinApp takes a `dbm.DB` and uses namespaced DBs for substores * [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. * MountStoreWithDB without providing a custom store works.
## 0.14.1 (April 9, 2018) ## 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 to allow mounting multiple stores with their own DB until they can share one
* [x/staking] Renamed to `simplestake` * [x/staking] Renamed to `simplestake`
* [builder] Functions don't take `passphrase` as argument * [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] `init` command outputs JSON of everything necessary for testnet
* [basecoind] `basecoin.db -> data/basecoin.db` * [basecoind] `basecoin.db -> data/basecoin.db`
* [basecli] `data/keys.db -> keys/keys.db` * [basecli] `data/keys.db -> keys/keys.db`

25
Gopkg.lock generated
View File

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

View File

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

View File

@ -1,13 +1,14 @@
PACKAGES=$(shell go list ./... | grep -v '/vendor/') 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) COMMIT_HASH := $(shell git rev-parse --short HEAD)
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}" 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
ci: get_tools get_vendor_deps build test_cover ci: get_tools get_vendor_deps install test_cover test_lint test
######################################## ########################################
### Build ### Build
@ -15,11 +16,11 @@ ci: get_tools get_vendor_deps build test_cover
# This can be unified later, here for easy demos # This can be unified later, here for easy demos
build: build:
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaiad go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaiacli go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli
else else
go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaiad go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaia/cmd/gaiad
go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaiacli go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaia/cmd/gaiacli
endif endif
build_examples: build_examples:
@ -36,8 +37,8 @@ else
endif endif
install: install:
go install $(BUILD_FLAGS) ./cmd/gaiad go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad
go install $(BUILD_FLAGS) ./cmd/gaiacli go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli
install_examples: install_examples:
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecoind go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecoind
@ -83,21 +84,22 @@ godocs:
######################################## ########################################
### Testing ### Testing
test: test_unit # test_cli test: test_unit test_cli
# Must be run in each package seperately for the visualization test_cli:
# Added here for easy reference @go test -count 1 -p 1 `go list github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test`
# coverage:
# go test -coverprofile=c.out && go tool cover -html=c.out
test_unit: test_unit:
@go test $(PACKAGES) @go test $(PACKAGES_NOCLITEST)
test_cover: test_cover:
@bash tests/test_cover.sh @bash tests/test_cover.sh
test_lint:
gometalinter --disable-all --enable='golint' --vendor ./...
benchmark: 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 # To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to. # unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html # 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" "github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
) )
// Key to store the header in the DB itself. // Key to store the header in the DB itself.
@ -29,6 +30,7 @@ type BaseApp struct {
db dbm.DB // common DB backend db dbm.DB // common DB backend
cms sdk.CommitMultiStore // Main (uncached) state cms sdk.CommitMultiStore // Main (uncached) state
router Router // handle any kind of message router Router // handle any kind of message
codespacer *sdk.Codespacer // handle module codespacing
// must be set // must be set
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
@ -55,14 +57,20 @@ var _ abci.Application = (*BaseApp)(nil)
// Create and name new BaseApp // Create and name new BaseApp
// NOTE: The db is used to store the version number for now. // NOTE: The db is used to store the version number for now.
func NewBaseApp(name string, logger log.Logger, db dbm.DB) *BaseApp { func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *BaseApp {
return &BaseApp{ app := &BaseApp{
Logger: logger, Logger: logger,
name: name, name: name,
db: db, db: db,
cms: store.NewCommitMultiStore(db), cms: store.NewCommitMultiStore(db),
router: NewRouter(), 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 // BaseApp Name
@ -70,6 +78,11 @@ func (app *BaseApp) Name() string {
return app.name 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 // Mount a store to the provided key in the BaseApp multistore
func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) { func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) {
for _, key := range keys { for _, key := range keys {
@ -87,10 +100,31 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) {
app.cms.MountStoreWithDB(key, typ, nil) app.cms.MountStoreWithDB(key, typ, nil)
} }
// nolint - Set functions // Set the txDecoder function
func (app *BaseApp) SetTxDecoder(txDecoder sdk.TxDecoder) { func (app *BaseApp) SetTxDecoder(txDecoder sdk.TxDecoder) {
app.txDecoder = 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) { func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) {
app.initChainer = initChainer app.initChainer = initChainer
} }
@ -103,7 +137,6 @@ func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) {
func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) { func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) {
app.anteHandler = ah app.anteHandler = ah
} }
func (app *BaseApp) Router() Router { return app.router } func (app *BaseApp) Router() Router { return app.router }
// load latest application version // 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. // 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 { func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context {
if isCheckTx { 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 { type state struct {
@ -195,7 +228,7 @@ func (app *BaseApp) setCheckState(header abci.Header) {
ms := app.cms.CacheMultiStore() ms := app.cms.CacheMultiStore()
app.checkState = &state{ app.checkState = &state{
ms: ms, 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() ms := app.cms.CacheMultiStore()
app.deliverState = &state{ app.deliverState = &state{
ms: ms, ms: ms,
ctx: sdk.NewContext(ms, header, false, nil), ctx: sdk.NewContext(ms, header, false, nil, app.Logger),
} }
} }
//---------------------------------------- //______________________________________________________________________________
// ABCI // ABCI
// Implements 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. // InitChain runs the initialization logic directly on the CommitMultiStore and commits it.
func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) { func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) {
if app.initChainer == nil { if app.initChainer == nil {
// TODO: should we have some default handling of validators?
return 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 // NOTE: we don't commit, but BeginBlock for block 1
// starts from this deliverState // starts from this deliverState
return return
} }
@ -311,9 +343,8 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
if result.IsOK() { if result.IsOK() {
app.valUpdates = append(app.valUpdates, result.ValidatorUpdates...) app.valUpdates = append(app.valUpdates, result.ValidatorUpdates...)
} else { } else {
// Even though the Code is not OK, there will be some side // Even though the Result.Code is not OK, there are still effects,
// effects, like those caused by fee deductions or sequence // namely fee deductions and sequence incrementing.
// incrementations.
} }
// Tell the blockchain engine (i.e. Tendermint). // 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) { func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) {
return app.runTx(true, nil, tx) 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. // Validate the Msg.
err := msg.ValidateBasic() err := msg.ValidateBasic()
if err != nil { if err != nil {
err = err.WithDefaultCodespace(sdk.CodespaceRoot)
return err.Result() return err.Result()
} }
@ -441,7 +473,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
// Use the header from this latest block. // Use the header from this latest block.
app.setCheckState(header) app.setCheckState(header)
// Emtpy the Deliver state // Empty the Deliver state
app.deliverState = nil app.deliverState = nil
return abci.ResponseCommit{ return abci.ResponseCommit{

View File

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

View File

@ -1,4 +1,4 @@
package core package context
import ( import (
"fmt" "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 // 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) passphrase, err := ctx.GetPassphraseFromStdin(name)
if err != nil { if err != nil {
return nil, err 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 // get the next sequence for the account address
func (c CoreContext) NextSequence(address []byte) (int64, error) { func (ctx CoreContext) NextSequence(address []byte) (int64, error) {
if c.Decoder == nil { if ctx.Decoder == nil {
return 0, errors.New("AccountDecoder required but not provided") 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 { if err != nil {
return 0, err 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 { if err != nil {
panic(err) panic(err)
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,6 @@ import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/pkg/errors"
keys "github.com/tendermint/go-crypto/keys" keys "github.com/tendermint/go-crypto/keys"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -15,7 +14,15 @@ var showKeysCmd = &cobra.Command{
Use: "show <name>", Use: "show <name>",
Short: "Show key info for the given name", Short: "Show key info for the given name",
Long: `Return public details of one local key.`, 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) { func getKey(name string) (keys.Info, error) {
@ -27,23 +34,10 @@ func getKey(name string) (keys.Info, error) {
return kb.Get(name) 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 // REST
// get key REST handler
func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) { func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
name := vars["name"] name := vars["name"]

View File

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

View File

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

View File

@ -10,7 +10,6 @@ import (
"os" "os"
"regexp" "regexp"
"testing" "testing"
"time"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -34,6 +33,7 @@ import (
keys "github.com/cosmos/cosmos-sdk/client/keys" keys "github.com/cosmos/cosmos-sdk/client/keys"
bapp "github.com/cosmos/cosmos-sdk/examples/basecoin/app" bapp "github.com/cosmos/cosmos-sdk/examples/basecoin/app"
btypes "github.com/cosmos/cosmos-sdk/examples/basecoin/types" btypes "github.com/cosmos/cosmos-sdk/examples/basecoin/types"
tests "github.com/cosmos/cosmos-sdk/tests"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
@ -42,9 +42,9 @@ var (
coinAmount = int64(10000000) coinAmount = int64(10000000)
// XXX bad globals // XXX bad globals
name = "test"
password = "0123456789"
port string // XXX: but it's the int ... port string // XXX: but it's the int ...
name string = "test"
password string = "0123456789"
seed string seed string
sendAddr 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)) jsonStr = []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed": "%s"}`, newName, newPassword, newSeed))
res, body = request(t, port, "POST", "/keys", jsonStr) 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 addr := body
assert.Len(t, addr, 40, "Returned address has wrong format", addr) 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) { func TestBlock(t *testing.T) {
waitForHeight(2) tests.WaitForHeight(2, port)
var resultBlock ctypes.ResultBlock var resultBlock ctypes.ResultBlock
@ -224,7 +224,7 @@ func TestCoinSend(t *testing.T) {
// create TX // create TX
receiveAddr, resultTx := doSend(t, port, seed) receiveAddr, resultTx := doSend(t, port, seed)
waitForHeight(resultTx.Height + 1) tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited // check if tx was commited
assert.Equal(t, uint32(0), resultTx.CheckTx.Code) assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
@ -253,7 +253,7 @@ func TestIBCTransfer(t *testing.T) {
// create TX // create TX
resultTx := doIBCTransfer(t, port, seed) resultTx := doIBCTransfer(t, port, seed)
waitForHeight(resultTx.Height + 1) tests.WaitForHeight(resultTx.Height+1, port)
// check if tx was commited // check if tx was commited
assert.Equal(t, uint32(0), resultTx.CheckTx.Code) assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
@ -286,7 +286,7 @@ func TestTxs(t *testing.T) {
// create TX // create TX
_, resultTx := doSend(t, port, seed) _, resultTx := doSend(t, port, seed)
waitForHeight(resultTx.Height + 1) tests.WaitForHeight(resultTx.Height+1, port)
// check if tx is findable // check if tx is findable
res, body := request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil) 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 // strt TM and the LCD in process, listening on their respective sockets
func startTMAndLCD() (*nm.Node, net.Listener, error) { 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()) // :( kb, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -376,7 +380,7 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
return nil, nil, err return nil, nil, err
} }
waitForStart() tests.WaitForStart(port)
return node, lcd, nil return node, lcd, nil
} }
@ -403,7 +407,7 @@ func startTM(cfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, p
} }
// wait for rpc // wait for rpc
waitForRPC() tests.WaitForRPC(GetConfig().RPC.ListenAddress)
logger.Info("Tendermint running!") logger.Info("Tendermint running!")
return n, err return n, err
@ -486,71 +490,3 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
return resultTx 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" cmn "github.com/tendermint/tmlibs/common"
client "github.com/cosmos/cosmos-sdk/client" client "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
keys "github.com/cosmos/cosmos-sdk/client/keys" keys "github.com/cosmos/cosmos-sdk/client/keys"
rpc "github.com/cosmos/cosmos-sdk/client/rpc" rpc "github.com/cosmos/cosmos-sdk/client/rpc"
tx "github.com/cosmos/cosmos-sdk/client/tx" tx "github.com/cosmos/cosmos-sdk/client/tx"
version "github.com/cosmos/cosmos-sdk/version" version "github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
auth "github.com/cosmos/cosmos-sdk/x/auth/rest" auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
bank "github.com/cosmos/cosmos-sdk/x/bank/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
ibc "github.com/cosmos/cosmos-sdk/x/ibc/rest" ibc "github.com/cosmos/cosmos-sdk/x/ibc/client/rest"
) )
const ( const (
@ -66,19 +67,21 @@ func startRESTServerFn(cdc *wire.Codec) func(cmd *cobra.Command, args []string)
func createHandler(cdc *wire.Codec) http.Handler { func createHandler(cdc *wire.Codec) http.Handler {
r := mux.NewRouter() r := mux.NewRouter()
r.HandleFunc("/version", version.VersionRequestHandler).Methods("GET") r.HandleFunc("/version", version.RequestHandler).Methods("GET")
kb, err := keys.GetKeyBase() //XXX kb, err := keys.GetKeyBase() //XXX
if err != nil { if err != nil {
panic(err) panic(err)
} }
ctx := context.NewCoreContextFromViper()
// TODO make more functional? aka r = keys.RegisterRoutes(r) // TODO make more functional? aka r = keys.RegisterRoutes(r)
keys.RegisterRoutes(r) keys.RegisterRoutes(r)
rpc.RegisterRoutes(r) rpc.RegisterRoutes(ctx, r)
tx.RegisterRoutes(r, cdc) tx.RegisterRoutes(ctx, r, cdc)
auth.RegisterRoutes(r, cdc, "main") auth.RegisterRoutes(ctx, r, cdc, "acc")
bank.RegisterRoutes(r, cdc, kb) bank.RegisterRoutes(ctx, r, cdc, kb)
ibc.RegisterRoutes(r, cdc, kb) ibc.RegisterRoutes(ctx, r, cdc, kb)
return r return r
} }

View File

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

View File

@ -6,6 +6,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
) )
const ( const (
@ -44,11 +45,12 @@ func initClientCommand() *cobra.Command {
return cmd return cmd
} }
func RegisterRoutes(r *mux.Router) { // Register REST endpoints
r.HandleFunc("/node_info", NodeInfoRequestHandler).Methods("GET") func RegisterRoutes(ctx context.CoreContext, r *mux.Router) {
r.HandleFunc("/syncing", NodeSyncingRequestHandler).Methods("GET") r.HandleFunc("/node_info", NodeInfoRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/blocks/latest", LatestBlockRequestHandler).Methods("GET") r.HandleFunc("/syncing", NodeSyncingRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/blocks/{height}", BlockRequestHandler).Methods("GET") r.HandleFunc("/blocks/latest", LatestBlockRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/validatorsets/latest", LatestValidatorsetRequestHandler).Methods("GET") r.HandleFunc("/blocks/{height}", BlockRequestHandlerFn(ctx)).Methods("GET")
r.HandleFunc("/validatorsets/{height}", ValidatorsetRequestHandler).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 return cmd
} }
func getNodeStatus() (*ctypes.ResultStatus, error) { func getNodeStatus(ctx context.CoreContext) (*ctypes.ResultStatus, error) {
// get the node // get the node
node, err := context.NewCoreContextFromViper().GetNode() node, err := ctx.GetNode()
if err != nil { if err != nil {
return &ctypes.ResultStatus{}, err return &ctypes.ResultStatus{}, err
} }
@ -34,7 +34,7 @@ func getNodeStatus() (*ctypes.ResultStatus, error) {
// CMD // CMD
func printNodeStatus(cmd *cobra.Command, args []string) error { func printNodeStatus(cmd *cobra.Command, args []string) error {
status, err := getNodeStatus() status, err := getNodeStatus(context.NewCoreContextFromViper())
if err != nil { if err != nil {
return err return err
} }
@ -51,8 +51,10 @@ func printNodeStatus(cmd *cobra.Command, args []string) error {
// REST // REST
func NodeInfoRequestHandler(w http.ResponseWriter, r *http.Request) { // REST handler for node info
status, err := getNodeStatus() func NodeInfoRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
status, err := getNodeStatus(ctx)
if err != nil { if err != nil {
w.WriteHeader(500) w.WriteHeader(500)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
@ -68,9 +70,12 @@ func NodeInfoRequestHandler(w http.ResponseWriter, r *http.Request) {
} }
w.Write(output) w.Write(output)
} }
}
func NodeSyncingRequestHandler(w http.ResponseWriter, r *http.Request) { // REST handler for node syncing
status, err := getNodeStatus() func NodeSyncingRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
status, err := getNodeStatus(ctx)
if err != nil { if err != nil {
w.WriteHeader(500) w.WriteHeader(500)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
@ -85,3 +90,4 @@ func NodeSyncingRequestHandler(w http.ResponseWriter, r *http.Request) {
} }
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" "github.com/cosmos/cosmos-sdk/client/context"
) )
// TODO these next two functions feel kinda hacky based on their placement
func validatorCommand() *cobra.Command { func validatorCommand() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "validatorset <height>", Use: "validatorset [height]",
Short: "Get the full validator set at given height", Short: "Get the full validator set at given height",
Args: cobra.MaximumNArgs(1),
RunE: printValidators, RunE: printValidators,
} }
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
@ -24,9 +27,9 @@ func validatorCommand() *cobra.Command {
return cmd return cmd
} }
func GetValidators(height *int64) ([]byte, error) { func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) {
// get the node // get the node
node, err := context.NewCoreContextFromViper().GetNode() node, err := ctx.GetNode()
if err != nil { if err != nil {
return nil, err 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 { if err != nil {
return err return err
} }
@ -70,7 +73,9 @@ func printValidators(cmd *cobra.Command, args []string) error {
// REST // REST
func ValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) { // 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) vars := mux.Vars(r)
height, err := strconv.ParseInt(vars["height"], 10, 64) height, err := strconv.ParseInt(vars["height"], 10, 64)
if err != nil { if err != nil {
@ -78,13 +83,13 @@ func ValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/validatorsets/{height}'.")) w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/validatorsets/{height}'."))
return return
} }
chainHeight, err := GetChainHeight() chainHeight, err := GetChainHeight(ctx)
if height > chainHeight { if height > chainHeight {
w.WriteHeader(404) w.WriteHeader(404)
w.Write([]byte("ERROR: Requested block height is bigger then the chain length.")) w.Write([]byte("ERROR: Requested block height is bigger then the chain length."))
return return
} }
output, err := GetValidators(&height) output, err := getValidators(ctx, &height)
if err != nil { if err != nil {
w.WriteHeader(500) w.WriteHeader(500)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
@ -92,15 +97,18 @@ func ValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
} }
w.Write(output) w.Write(output)
} }
}
func LatestValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) { // Latest Validator Set REST handler
height, err := GetChainHeight() func LatestValidatorSetRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
height, err := GetChainHeight(ctx)
if err != nil { if err != nil {
w.WriteHeader(500) w.WriteHeader(500)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
return return
} }
output, err := GetValidators(&height) output, err := getValidators(ctx, &height)
if err != nil { if err != nil {
w.WriteHeader(500) w.WriteHeader(500)
w.Write([]byte(err.Error())) w.Write([]byte(err.Error()))
@ -108,3 +116,4 @@ func LatestValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
} }
w.Write(output) w.Write(output)
} }
}

View File

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

View File

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

View File

@ -4,26 +4,22 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/wire" "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 // AddCommands adds a number of tx-query related subcommands
func AddCommands(cmd *cobra.Command, cdc *wire.Codec) { func AddCommands(cmd *cobra.Command, cdc *wire.Codec) {
cmdr := commander{cdc}
cmd.AddCommand( cmd.AddCommand(
SearchTxCmd(cmdr), SearchTxCmd(cdc),
QueryTxCmd(cmdr), 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", SearchTxRequestHandler(cdc)).Methods("GET")
r.HandleFunc("/txs/{hash}", QueryTxRequestHandler(cdc)).Methods("GET")
// r.HandleFunc("/txs/sign", SignTxRequstHandler).Methods("POST") // r.HandleFunc("/txs/sign", SignTxRequstHandler).Methods("POST")
// r.HandleFunc("/txs/broadcast", BroadcastTxRequestHandler).Methods("POST") // r.HandleFunc("/txs/broadcast", BroadcastTxRequestHandler).Methods("POST")
} }

View File

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

View File

@ -8,12 +8,15 @@ import (
keys "github.com/tendermint/go-crypto/keys" keys "github.com/tendermint/go-crypto/keys"
) )
// REST request body
// TODO does this need to be exposed?
type SignTxBody struct { type SignTxBody struct {
Name string `json="name"` Name string `json="name"`
Password string `json="password"` Password string `json="password"`
TxBytes string `json="tx"` TxBytes string `json="tx"`
} }
// sign transaction REST Handler
func SignTxRequstHandler(w http.ResponseWriter, r *http.Request) { func SignTxRequstHandler(w http.ResponseWriter, r *http.Request) {
var kb keys.Keybase var kb keys.Keybase
var m SignTxBody 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 package main
import ( import (
"os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/tendermint/tmlibs/cli" "github.com/tendermint/tmlibs/cli"
@ -12,19 +10,15 @@ import (
"github.com/cosmos/cosmos-sdk/client/lcd" "github.com/cosmos/cosmos-sdk/client/lcd"
"github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands" ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli"
simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands" stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
"github.com/cosmos/cosmos-sdk/examples/basecoin/app" "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
) )
// TODO: distinguish from basecli
// rootCmd is the entry point for this binary // rootCmd is the entry point for this binary
var ( var (
rootCmd = &cobra.Command{ rootCmd = &cobra.Command{
@ -34,10 +28,7 @@ var (
) )
func main() { func main() {
// disable sorting
cobra.EnableCommandSorting = false cobra.EnableCommandSorting = false
// get the codec
cdc := app.MakeCodec() cdc := app.MakeCodec()
// TODO: setup keybase, viper object, etc. to be passed into // TODO: setup keybase, viper object, etc. to be passed into
@ -53,24 +44,21 @@ func main() {
// add query/post commands (custom to binary) // add query/post commands (custom to binary)
rootCmd.AddCommand( rootCmd.AddCommand(
client.GetCommands( 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( rootCmd.AddCommand(
client.PostCommands( client.PostCommands(
bankcmd.SendTxCmd(cdc), bankcmd.SendTxCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCTransferCmd(cdc), ibccmd.IBCTransferCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCRelayCmd(cdc), ibccmd.IBCRelayCmd(cdc),
simplestakingcmd.BondTxCmd(cdc), stakecmd.GetCmdDeclareCandidacy(cdc),
)...) stakecmd.GetCmdEditCandidacy(cdc),
rootCmd.AddCommand( stakecmd.GetCmdDelegate(cdc),
client.PostCommands( stakecmd.GetCmdUnbond(cdc),
simplestakingcmd.UnbondTxCmd(cdc),
)...) )...)
// add proxy, version and key info // add proxy, version and key info
@ -83,6 +71,6 @@ func main() {
) )
// prepare and add flags // prepare and add flags
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.gaiacli")) executor := cli.PrepareMainCmd(rootCmd, "GA", app.DefaultCLIHome)
executor.Execute() 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. # General information about the project.
project = u'Cosmos-SDK' project = u'Cosmos-SDK'
copyright = u'2017, The Authors' copyright = u'2018, The Authors'
author = u'The Authors' author = u'The Authors'
# The version info for the project you're documenting, acts as replacement for # 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 # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path # 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. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx' pygments_style = 'sphinx'

View File

@ -29,9 +29,6 @@ type Msg interface {
// Must be alphanumeric or empty. // Must be alphanumeric or empty.
Type() string Type() string
// Get some property of the Msg.
Get(key interface{}) (value interface{})
// Get the canonical byte representation of the Msg. // Get the canonical byte representation of the Msg.
GetSignBytes() []byte 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 method to enforce that message contents are well formed before any actual logic
begins. 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`: For instance, the `Basecoin` message types are defined in `x/bank/tx.go`:
```go ```go
type SendMsg struct { type MsgSend struct {
Inputs []Input `json:"inputs"` Inputs []Input `json:"inputs"`
Outputs []Output `json:"outputs"` Outputs []Output `json:"outputs"`
} }
type IssueMsg struct { type MsgIssue struct {
Banker sdk.Address `json:"banker"` Banker sdk.Address `json:"banker"`
Outputs []Output `json:"outputs"` Outputs []Output `json:"outputs"`
} }
@ -83,7 +77,7 @@ type IssueMsg struct {
Each specifies the addresses that must sign the message: Each specifies the addresses that must sign the message:
```go ```go
func (msg SendMsg) GetSigners() []sdk.Address { func (msg MsgSend) GetSigners() []sdk.Address {
addrs := make([]sdk.Address, len(msg.Inputs)) addrs := make([]sdk.Address, len(msg.Inputs))
for i, in := range msg.Inputs { for i, in := range msg.Inputs {
addrs[i] = in.Address addrs[i] = in.Address
@ -91,7 +85,7 @@ func (msg SendMsg) GetSigners() []sdk.Address {
return addrs return addrs
} }
func (msg IssueMsg) GetSigners() []sdk.Address { func (msg MsgIssue) GetSigners() []sdk.Address {
return []sdk.Address{msg.Banker} 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 relevant types to be registered ahead of type. Registration happens on a
`Codec` object, so as not to taint the global name space. `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: types:
```go ```go
cdc.RegisterInterface((*sdk.Msg)(nil), nil) cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(bank.SendMsg{}, "cosmos-sdk/SendMsg", nil) cdc.RegisterConcrete(bank.MsgSend{}, "cosmos-sdk/MsgSend", nil)
cdc.RegisterConcrete(bank.IssueMsg{}, "cosmos-sdk/IssueMsg", nil) cdc.RegisterConcrete(bank.MsgIssue{}, "cosmos-sdk/MsgIssue", nil)
``` ```
Note how each concrete type is given a name - these name determine the type's 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 The quintessential SDK application is Basecoin - a simple
multi-asset cryptocurrency. Basecoin consists of a set of multi-asset cryptocurrency. Basecoin consists of a set of
accounts stored in a Merkle tree, where each account may have accounts stored in a Merkle tree, where each account may have
many coins. There are two message types: SendMsg and IssueMsg. many coins. There are two message types: MsgSend and MsgIssue.
SendMsg allows coins to be sent around, while IssueMsg allows a MsgSend allows coins to be sent around, while MsgIssue allows a
set of predefined users to issue new coins. set of predefined users to issue new coins.
## Conclusion ## Conclusion

View File

@ -14,14 +14,13 @@ Welcome to the Cosmos SDK!
SDK SDK
--- ---
.. One maxdepth for now
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
sdk/overview.rst
sdk/install.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 .. Basecoin
.. -------- .. --------
@ -29,19 +28,17 @@ SDK
.. .. toctree:: .. .. toctree::
:maxdepth: 2 :maxdepth: 2
.. basecoin/basics.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
.. basecoin/extensions.rst
Extensions .. Extensions
---------- .. ----------
Replay Protection .. old/basecoin/extensions.rst # probably not worth salvaging
~~~~~~~~~~~~~~~~~
.. toctree:: .. Replay Protection
:maxdepth: 1 .. ~~~~~~~~~~~~~~~~~
x/replay-protection.rst .. old/replay-protection.rst # not sure if worth salvaging
Staking Staking
@ -50,17 +47,13 @@ Staking
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
staking/intro.rst staking/testnet.rst
staking/key-management.rst .. staking/intro.rst
staking/local-testnet.rst .. staking/key-management.rst
staking/public-testnet.rst .. staking/local-testnet.rst
.. staking/public-testnet.rst
Extras .. IBC
------ .. ---
.. One maxdepth for now .. old/ibc.rst # needs to be updated
.. toctree::
:maxdepth: 1
ibc.rst

View File

@ -12,7 +12,7 @@ Get Tokens
If you haven't already `created a key <../key-management.html>`__, If you haven't already `created a key <../key-management.html>`__,
do so now. Copy your key's address and enter it into do so now. Copy your key's address and enter it into
`this utility <http://www.cosmosvalidators.com/>`__ which will send you `this utility <http://www.cosmosvalidators.com/>`__ which will send you
some ``fermion`` testnet tokens. some ``steak`` testnet tokens.
Get Files Get Files
--------- ---------
@ -59,6 +59,6 @@ and check our balance:
Where ``$MYADDR`` is the address originally generated by ``gaia keys new bob``. 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 `staking module overview <./staking-module.html>`__ for more information
on using the ``gaia client``. 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 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. 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 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`. 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, The former is a global validity check (checking nonces, sigs and sufficient balances to pay fees,

View File

@ -1,16 +1,12 @@
Install Install
======= =======
If you aren't used to compile go programs and just want the released Cosmos SDK can be installed to
version of the code, please head to our ``$GOPATH/src/github.com/cosmos/cosmos-sdk`` like a normal Go program:
`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:
:: ::
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 If the dependencies have been updated with breaking changes, or if
another branch is required, ``dep`` is used for dependency management. 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 cd $GOPATH/src/github.com/cosmos/cosmos-sdk
git pull origin master make get_vendor_deps
make all make install
make install_examples
This will create the ``basecoin`` binary in ``$GOPATH/bin``. This will install ``gaiad`` and ``gaiacli`` and four example binaries:
``make all`` implies ``make get_vendor_deps`` and uses ``dep`` to ``basecoind``, ``basecli``, ``democoind``, and ``democli``.
install the correct version of all dependencies. It also tests the code,
including some cli tests to make sure your binary behaves properly.
If you need another branch, make sure to run ``git checkout <branch>`` Verify that everything is OK by running:
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. 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: properties:
denom: denom:
type: string type: string
example: fermion example: steak
amount: amount:
type: number type: number
example: 50 example: 50

View File

@ -143,9 +143,6 @@ implementing the ``Msg`` interface:
// Must be alphanumeric or empty. // Must be alphanumeric or empty.
Type() string Type() string
// Get some property of the Msg.
Get(key interface{}) (value interface{})
// Get the canonical byte representation of the Msg. // Get the canonical byte representation of the Msg.
GetSignBytes() []byte 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 method to enforce that message contents are well formed before any actual logic
begins. 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``: For instance, the ``Basecoin`` message types are defined in ``x/bank/tx.go``:
:: ::

View File

@ -1,10 +1,17 @@
# IBC Specification # 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 ## 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 | | BaseApp Terms | IBC Terms |
| ------------- | ---------- | | ------------- | ---------- |
@ -12,20 +19,27 @@ How IBC module treats incoming IBC packets is simillar with how BaseApp treats i
| Tx | Packet | | Tx | Packet |
| Msg | Payload | | Msg | Payload |
## MVP Specifications ## MVP Specifications
### [MVP1](./mvp1.md) ### [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) ### [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) ### [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) ### [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 This project is a demonstration of the Cosmos Hub staking functionality; it is
designed to get validator acquianted with staking concepts and procedure. designed to get validator acquianted with staking concepts and procedures.
Potential validators will be declaring their candidacy, after which users can 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 delegate and, if they so wish, unbond. This can be practiced using a local or
public testnet. 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 Install
------- -------
The ``gaia`` tooling is an extension of the Cosmos-SDK; to install: The ``gaiad`` and ``gaiacli`` binaries:
:: ::
go get github.com/cosmos/gaia go get github.com/cosmos/cosmos-sdk
cd $GOPATH/src/github.com/cosmos/gaia cd $GOPATH/src/github.com/cosmos/cosmos-sdk
make get_vendor_deps make get_vendor_deps
make install make install
It has three primary commands: Let's jump right into it. First, we initialize some default files:
:: ::
Available Commands: gaiad init
node The Cosmos Network delegation-game blockchain test
rest-server REST client for gaia commands
client Gaia light client
version Show version info which will output:
help Help about any command
and a handful of flags that are highlighted only as necessary. ::
The ``gaia node`` command is a proxt for running a tendermint node. You'll be using I[03-30|11:20:13.365] Found private validator module=main path=/root/.gaiad/config/priv_validator.json
this command to either initialize a new node, or - using existing files - joining I[03-30|11:20:13.365] Found genesis file module=main path=/root/.gaiad/config/genesis.json
the testnet. Secret phrase to access coins:
citizen hungry tennis noise park hire glory exercise link glow dolphin labor design grit apple abandon
The ``gaia rest-server`` command is used by the `cosmos UI <https://github.com/cosmos/cosmos-ui>`__. 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.
Lastly, the ``gaia client`` command is the workhorse of the staking module. It allows 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:
for sending various transactions and other types of interaction with a running chain.
that you've setup or joined a testnet.
Generating Keys ::
---------------
Review the `key management tutorial <../key-management.html>`__ and create one key gaiacli keys add alice --recover
if you'll be joining the public testnet, and three keys if you'll be trying out a local
testnet. 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 Setup Testnet
------------- -------------
The first thing you'll want to do is either `create a local testnet <./local-testnet.html>`__ or Next, we start the daemon (do this in another window):
join a `public testnet <./public-testnet.html>`__. Either step is required before proceeding.
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 gaiad start
testnet, the token amounts will need to be adjusted.
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 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 "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 Adding a Second Validator
------------------------- -------------------------
**This section is wrong/needs to be updated**
Next, let's add the second node as a validator. Next, let's add the second node as a validator.
First, we need the pub_key data: First, we need the pub_key data:
** need to make bob a priv_Val above?
:: ::
cat $HOME/.gaia2/priv_validator.json 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: 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: 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 there aren't enough validators online. Turn it back on and they will
start streaming again. 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 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: You'll see output like:
@ -194,13 +328,13 @@ You'll see output like:
"height": 51585 "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: 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: 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: 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> gaiacli unbond --amount=5mycoin --name=charlie --pubkey=<pub_key data>
gaia client query account 48F74F48281C89E5E4BE9092F735EA519768E8EF gaiacli account 48F74F48281C89E5E4BE9092F735EA519768E8EF
See the bond decrease with ``gaia client query delegator-bond`` like above. See the bond decrease with ``gaiacli query delegator-bond`` like above.
That concludes an overview of the ``gaia`` tooling for local testing.

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/auth"
"github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc" "github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/simplestake" "github.com/cosmos/cosmos-sdk/x/stake"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types" "github.com/cosmos/cosmos-sdk/examples/basecoin/types"
) )
@ -29,16 +29,16 @@ type BasecoinApp struct {
cdc *wire.Codec cdc *wire.Codec
// keys to access the substores // keys to access the substores
capKeyMainStore *sdk.KVStoreKey keyMain *sdk.KVStoreKey
capKeyAccountStore *sdk.KVStoreKey keyAccount *sdk.KVStoreKey
capKeyIBCStore *sdk.KVStoreKey keyIBC *sdk.KVStoreKey
capKeyStakingStore *sdk.KVStoreKey keyStake *sdk.KVStoreKey
// Manage getting and setting accounts // Manage getting and setting accounts
accountMapper sdk.AccountMapper accountMapper sdk.AccountMapper
coinKeeper bank.Keeper
// Handle fees ibcMapper ibc.Mapper
feeHandler sdk.FeeHandler stakeKeeper stake.Keeper
} }
func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { 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. // Create your application object.
var app = &BasecoinApp{ var app = &BasecoinApp{
BaseApp: bam.NewBaseApp(appName, logger, db), BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
cdc: cdc, cdc: cdc,
capKeyMainStore: sdk.NewKVStoreKey("main"), keyMain: sdk.NewKVStoreKey("main"),
capKeyAccountStore: sdk.NewKVStoreKey("acc"), keyAccount: sdk.NewKVStoreKey("acc"),
capKeyIBCStore: sdk.NewKVStoreKey("ibc"), keyIBC: sdk.NewKVStoreKey("ibc"),
capKeyStakingStore: sdk.NewKVStoreKey("stake"), keyStake: sdk.NewKVStoreKey("stake"),
} }
// Define the accountMapper. // Define the accountMapper.
app.accountMapper = auth.NewAccountMapper( app.accountMapper = auth.NewAccountMapper(
cdc, cdc,
app.capKeyMainStore, // target store app.keyAccount, // target store
&types.AppAccount{}, // prototype &types.AppAccount{}, // prototype
).Seal() )
// Add handlers. // add accountMapper/handlers
coinKeeper := bank.NewCoinKeeper(app.accountMapper) app.coinKeeper = bank.NewKeeper(app.accountMapper)
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore) app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper) app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
// register message routes
app.Router(). app.Router().
AddRoute("bank", bank.NewHandler(coinKeeper)). AddRoute("bank", bank.NewHandler(app.coinKeeper)).
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)). AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper)) AddRoute("stake", stake.NewHandler(app.stakeKeeper))
// Define the feeHandler.
app.feeHandler = auth.BurnFeeHandler
// Initialize BaseApp. // Initialize BaseApp.
app.SetTxDecoder(app.txDecoder)
app.SetInitChainer(app.initChainer) app.SetInitChainer(app.initChainer)
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeyIBCStore, app.capKeyStakingStore) app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeHandler)) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, auth.BurnFeeHandler))
err := app.LoadLatestVersion(app.capKeyMainStore) err := app.LoadLatestVersion(app.keyMain)
if err != nil { if err != nil {
cmn.Exit(err.Error()) cmn.Exit(err.Error())
} }
return app return app
} }
// Custom tx codec // Custom tx codec
func MakeCodec() *wire.Codec { func MakeCodec() *wire.Codec {
var cdc = wire.NewCodec() 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 // register custom AppAccount
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
cdc.RegisterInterface((*sdk.Account)(nil), nil) cdc.RegisterInterface((*sdk.Account)(nil), nil)
cdc.RegisterConcrete(&types.AppAccount{}, "basecoin/Account", nil) cdc.RegisterConcrete(&types.AppAccount{}, "basecoin/Account", nil)
// Register crypto.
wire.RegisterCrypto(cdc)
return 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 // Custom logic for basecoin initialization
func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes stateJSON := req.AppStateBytes
genesisState := new(types.GenesisState) genesisState := new(types.GenesisState)
err := json.Unmarshal(stateJSON, genesisState) err := app.cdc.UnmarshalJSON(stateJSON, genesisState)
if err != nil { if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "") // return sdk.ErrGenesisParse("").TraceCause(err, "")
@ -149,3 +121,25 @@ func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain)
} }
return abci.ResponseInitChain{} 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, 0,
} }
sendMsg1 = bank.SendMsg{ sendMsg1 = bank.MsgSend{
Inputs: []bank.Input{bank.NewInput(addr1, coins)}, Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{bank.NewOutput(addr2, coins)}, Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
} }
sendMsg2 = bank.SendMsg{ sendMsg2 = bank.MsgSend{
Inputs: []bank.Input{bank.NewInput(addr1, coins)}, Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{ Outputs: []bank.Output{
bank.NewOutput(addr2, halfCoins), bank.NewOutput(addr2, halfCoins),
@ -55,7 +55,7 @@ var (
}, },
} }
sendMsg3 = bank.SendMsg{ sendMsg3 = bank.MsgSend{
Inputs: []bank.Input{ Inputs: []bank.Input{
bank.NewInput(addr1, coins), bank.NewInput(addr1, coins),
bank.NewInput(addr4, coins), bank.NewInput(addr4, coins),
@ -66,7 +66,7 @@ var (
}, },
} }
sendMsg4 = bank.SendMsg{ sendMsg4 = bank.MsgSend{
Inputs: []bank.Input{ Inputs: []bank.Input{
bank.NewInput(addr2, coins), bank.NewInput(addr2, coins),
}, },
@ -75,7 +75,7 @@ var (
}, },
} }
sendMsg5 = bank.SendMsg{ sendMsg5 = bank.MsgSend{
Inputs: []bank.Input{ Inputs: []bank.Input{
bank.NewInput(addr1, manyCoins), bank.NewInput(addr1, manyCoins),
}, },
@ -166,7 +166,7 @@ func TestSortGenesis(t *testing.T) {
// Unsorted coins means invalid // Unsorted coins means invalid
err := sendMsg5.ValidateBasic() 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 // Sort coins, should be valid
sendMsg5.Inputs[0].Coins.Sort() sendMsg5.Inputs[0].Coins.Sort()
@ -208,7 +208,7 @@ func TestGenesis(t *testing.T) {
assert.Equal(t, acc, res1) assert.Equal(t, acc, res1)
} }
func TestSendMsgWithAccounts(t *testing.T) { func TestMsgSendWithAccounts(t *testing.T) {
bapp := newBasecoinApp() bapp := newBasecoinApp()
// Construct some genesis bytes to reflect basecoin/types/AppAccount // Construct some genesis bytes to reflect basecoin/types/AppAccount
@ -243,13 +243,13 @@ func TestSendMsgWithAccounts(t *testing.T) {
tx.Signatures[0].Sequence = 1 tx.Signatures[0].Sequence = 1
res := bapp.Deliver(tx) 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 // resigning the tx with the bumped sequence should work
SignCheckDeliver(t, bapp, sendMsg1, []int64{1}, true, priv1) SignCheckDeliver(t, bapp, sendMsg1, []int64{1}, true, priv1)
} }
func TestSendMsgMultipleOut(t *testing.T) { func TestMsgSendMultipleOut(t *testing.T) {
bapp := newBasecoinApp() bapp := newBasecoinApp()
genCoins, err := sdk.ParseCoins("42foocoin") genCoins, err := sdk.ParseCoins("42foocoin")
@ -311,7 +311,7 @@ func TestSengMsgMultipleInOut(t *testing.T) {
CheckBalance(t, bapp, addr3, "10foocoin") CheckBalance(t, bapp, addr3, "10foocoin")
} }
func TestSendMsgDependent(t *testing.T) { func TestMsgSendDependent(t *testing.T) {
bapp := newBasecoinApp() bapp := newBasecoinApp()
genCoins, err := sdk.ParseCoins("42foocoin") genCoins, err := sdk.ParseCoins("42foocoin")
@ -339,7 +339,7 @@ func TestSendMsgDependent(t *testing.T) {
CheckBalance(t, bapp, addr1, "42foocoin") CheckBalance(t, bapp, addr1, "42foocoin")
} }
func TestQuizMsg(t *testing.T) { func TestMsgQuiz(t *testing.T) {
bapp := newBasecoinApp() bapp := newBasecoinApp()
// Construct genesis state // Construct genesis state
@ -437,18 +437,18 @@ func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq []int64,
// Run a Check // Run a Check
res := bapp.Check(tx) res := bapp.Check(tx)
if expPass { if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log) require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else { } else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log) require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
} }
// Simulate a Block // Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{}) bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx) res = bapp.Deliver(tx)
if expPass { if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log) require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else { } else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log) require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
} }
bapp.EndBlock(abci.RequestEndBlock{}) bapp.EndBlock(abci.RequestEndBlock{})
//bapp.Commit() //bapp.Commit()

View File

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

View File

@ -1,8 +1,8 @@
package main package main
import ( import (
"encoding/json"
"os" "os"
"path/filepath"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -15,31 +15,31 @@ import (
"github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server"
) )
// rootCmd is the entry point for this binary func main() {
var ( cdc := app.MakeCodec()
context = server.NewDefaultContext() ctx := server.NewDefaultContext()
rootCmd = &cobra.Command{
rootCmd := &cobra.Command{
Use: "basecoind", Use: "basecoind",
Short: "Basecoin Daemon (server)", 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(ctx, cdc, rootCmd, server.DefaultAppInit,
server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context) server.ConstructAppCreator(newApp, "basecoin"),
server.ConstructAppExporter(exportAppState, "basecoin"))
// prepare and add flags // prepare and add flags
rootDir := os.ExpandEnv("$HOME/.basecoind") rootDir := os.ExpandEnv("$HOME/.basecoind")
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir) executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
executor.Execute() 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/auth"
"github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc" "github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/simplestake"
"github.com/cosmos/cosmos-sdk/examples/democoin/types" "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/cool"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow" "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" "github.com/cosmos/cosmos-sdk/examples/democoin/x/sketchy"
) )
@ -38,11 +38,15 @@ type DemocoinApp struct {
capKeyIBCStore *sdk.KVStoreKey capKeyIBCStore *sdk.KVStoreKey
capKeyStakingStore *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 // Manage getting and setting accounts
accountMapper sdk.AccountMapper accountMapper sdk.AccountMapper
// Handle fees
feeHandler sdk.FeeHandler
} }
func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp { 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. // Create your application object.
var app = &DemocoinApp{ var app = &DemocoinApp{
BaseApp: bam.NewBaseApp(appName, logger, db), BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
cdc: cdc, cdc: cdc,
capKeyMainStore: sdk.NewKVStoreKey("main"), capKeyMainStore: sdk.NewKVStoreKey("main"),
capKeyAccountStore: sdk.NewKVStoreKey("acc"), capKeyAccountStore: sdk.NewKVStoreKey("acc"),
@ -64,90 +68,59 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
// Define the accountMapper. // Define the accountMapper.
app.accountMapper = auth.NewAccountMapper( app.accountMapper = auth.NewAccountMapper(
cdc, cdc,
app.capKeyMainStore, // target store app.capKeyAccountStore, // target store
&types.AppAccount{}, // prototype &types.AppAccount{}, // prototype
).Seal() )
// Add handlers. // Add handlers.
coinKeeper := bank.NewCoinKeeper(app.accountMapper) app.coinKeeper = bank.NewKeeper(app.accountMapper)
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper) app.coolKeeper = cool.NewKeeper(app.capKeyMainStore, app.coinKeeper, app.RegisterCodespace(cool.DefaultCodespace))
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper) app.powKeeper = pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), app.coinKeeper, app.RegisterCodespace(pow.DefaultCodespace))
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore) app.ibcMapper = ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace))
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper) app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace))
app.Router(). app.Router().
AddRoute("bank", bank.NewHandler(coinKeeper)). AddRoute("bank", bank.NewHandler(app.coinKeeper)).
AddRoute("cool", cool.NewHandler(coolKeeper)). AddRoute("cool", cool.NewHandler(app.coolKeeper)).
AddRoute("pow", powKeeper.Handler). AddRoute("pow", app.powKeeper.Handler).
AddRoute("sketchy", sketchy.NewHandler()). AddRoute("sketchy", sketchy.NewHandler()).
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)). AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper)) AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper))
// Define the feeHandler.
app.feeHandler = auth.BurnFeeHandler
// Initialize BaseApp. // Initialize BaseApp.
app.SetTxDecoder(app.txDecoder) app.SetInitChainer(app.initChainerFn(app.coolKeeper, app.powKeeper))
app.SetInitChainer(app.initChainerFn(coolKeeper, powKeeper))
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeyPowStore, app.capKeyIBCStore, app.capKeyStakingStore) 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) err := app.LoadLatestVersion(app.capKeyMainStore)
if err != nil { if err != nil {
cmn.Exit(err.Error()) cmn.Exit(err.Error())
} }
return app return app
} }
// custom tx codec // custom tx codec
func MakeCodec() *wire.Codec { func MakeCodec() *wire.Codec {
var cdc = wire.NewCodec() var cdc = wire.NewCodec()
wire.RegisterCrypto(cdc) // Register crypto.
// Register Msgs sdk.RegisterWire(cdc) // Register Msgs
cdc.RegisterInterface((*sdk.Msg)(nil), nil) cool.RegisterWire(cdc)
cdc.RegisterConcrete(bank.SendMsg{}, "democoin/Send", nil) pow.RegisterWire(cdc)
cdc.RegisterConcrete(bank.IssueMsg{}, "democoin/Issue", nil) bank.RegisterWire(cdc)
cdc.RegisterConcrete(cool.QuizMsg{}, "democoin/Quiz", nil) ibc.RegisterWire(cdc)
cdc.RegisterConcrete(cool.SetTrendMsg{}, "democoin/SetTrend", nil) simplestake.RegisterWire(cdc)
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)
// Register AppAccount // Register AppAccount
cdc.RegisterInterface((*sdk.Account)(nil), nil) cdc.RegisterInterface((*sdk.Account)(nil), nil)
cdc.RegisterConcrete(&types.AppAccount{}, "democoin/Account", nil) cdc.RegisterConcrete(&types.AppAccount{}, "democoin/Account", nil)
// Register crypto.
wire.RegisterCrypto(cdc)
return 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 // custom logic for democoin initialization
func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keeper) sdk.InitChainer { func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes stateJSON := req.AppStateBytes
genesisState := new(types.GenesisState) genesisState := new(types.GenesisState)
err := json.Unmarshal(stateJSON, genesisState) err := app.cdc.UnmarshalJSON(stateJSON, genesisState)
if err != nil { if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "") // return sdk.ErrGenesisParse("").TraceCause(err, "")
@ -163,13 +136,13 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep
} }
// Application specific genesis handling // Application specific genesis handling
err = coolKeeper.InitGenesis(ctx, genesisState.CoolGenesis) err = cool.InitGenesis(ctx, app.coolKeeper, genesisState.CoolGenesis)
if err != nil { if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "") // return sdk.ErrGenesisParse("").TraceCause(err, "")
} }
err = powKeeper.InitGenesis(ctx, genesisState.PowGenesis) err = pow.InitGenesis(ctx, app.powKeeper, genesisState.POWGenesis)
if err != nil { if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "") // return sdk.ErrGenesisParse("").TraceCause(err, "")
@ -178,3 +151,27 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep
return abci.ResponseInitChain{} 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, 0,
} }
sendMsg = bank.SendMsg{ sendMsg = bank.MsgSend{
Inputs: []bank.Input{bank.NewInput(addr1, coins)}, Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{bank.NewOutput(addr2, coins)}, Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
} }
quizMsg1 = cool.QuizMsg{ quizMsg1 = cool.MsgQuiz{
Sender: addr1, Sender: addr1,
CoolAnswer: "icecold", CoolAnswer: "icecold",
} }
quizMsg2 = cool.QuizMsg{ quizMsg2 = cool.MsgQuiz{
Sender: addr1, Sender: addr1,
CoolAnswer: "badvibesonly", CoolAnswer: "badvibesonly",
} }
setTrendMsg1 = cool.SetTrendMsg{ setTrendMsg1 = cool.MsgSetTrend{
Sender: addr1, Sender: addr1,
Cool: "icecold", Cool: "icecold",
} }
setTrendMsg2 = cool.SetTrendMsg{ setTrendMsg2 = cool.MsgSetTrend{
Sender: addr1, Sender: addr1,
Cool: "badvibesonly", Cool: "badvibesonly",
} }
setTrendMsg3 = cool.SetTrendMsg{ setTrendMsg3 = cool.MsgSetTrend{
Sender: addr1, Sender: addr1,
Cool: "warmandkind", Cool: "warmandkind",
} }
@ -157,7 +157,7 @@ func TestGenesis(t *testing.T) {
assert.Equal(t, acc, res1) assert.Equal(t, acc, res1)
} }
func TestSendMsgWithAccounts(t *testing.T) { func TestMsgSendWithAccounts(t *testing.T) {
bapp := newDemocoinApp() bapp := newDemocoinApp()
// Construct some genesis bytes to reflect democoin/types/AppAccount // Construct some genesis bytes to reflect democoin/types/AppAccount
@ -202,12 +202,12 @@ func TestSendMsgWithAccounts(t *testing.T) {
// Run a Check // Run a Check
res := bapp.Check(tx) 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 // Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{}) bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx) res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log) assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
// Check balances // Check balances
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{}) ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
@ -218,22 +218,22 @@ func TestSendMsgWithAccounts(t *testing.T) {
// Delivering again should cause replay error // Delivering again should cause replay error
res = bapp.Deliver(tx) 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 // bumping the txnonce number without resigning should be an auth error
tx.Signatures[0].Sequence = 1 tx.Signatures[0].Sequence = 1
res = bapp.Deliver(tx) 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 // resigning the tx with the bumped sequence should work
sequences = []int64{1} sequences = []int64{1}
sig = priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, tx.Msg)) sig = priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, tx.Msg))
tx.Signatures[0].Signature = sig tx.Signatures[0].Signature = sig
res = bapp.Deliver(tx) 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() bapp := newDemocoinApp()
// Construct genesis state // Construct genesis state
@ -271,11 +271,11 @@ func TestMineMsg(t *testing.T) {
assert.Equal(t, acc1, res1) assert.Equal(t, acc1, res1)
// Mine and check for reward // Mine and check for reward
mineMsg1 := pow.GenerateMineMsg(addr1, 1, 2) mineMsg1 := pow.GenerateMsgMine(addr1, 1, 2)
SignCheckDeliver(t, bapp, mineMsg1, 0, true) SignCheckDeliver(t, bapp, mineMsg1, 0, true)
CheckBalance(t, bapp, "1pow") CheckBalance(t, bapp, "1pow")
// Mine again and check for reward // Mine again and check for reward
mineMsg2 := pow.GenerateMineMsg(addr1, 2, 3) mineMsg2 := pow.GenerateMsgMine(addr1, 2, 3)
SignCheckDeliver(t, bapp, mineMsg2, 1, true) SignCheckDeliver(t, bapp, mineMsg2, 1, true)
CheckBalance(t, bapp, "2pow") CheckBalance(t, bapp, "2pow")
// Mine again - should be invalid // 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() bapp := newDemocoinApp()
// Construct genesis state // Construct genesis state
@ -403,18 +403,18 @@ func SignCheckDeliver(t *testing.T, bapp *DemocoinApp, msg sdk.Msg, seq int64, e
// Run a Check // Run a Check
res := bapp.Check(tx) res := bapp.Check(tx)
if expPass { if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log) require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else { } else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log) require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
} }
// Simulate a Block // Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{}) bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx) res = bapp.Deliver(tx)
if expPass { if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log) require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else { } else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log) require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
} }
bapp.EndBlock(abci.RequestEndBlock{}) bapp.EndBlock(abci.RequestEndBlock{})
//bapp.Commit() //bapp.Commit()

View File

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

View File

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

View File

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

View File

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

View File

@ -6,12 +6,15 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
// Cool errors reserve 400 ~ 499.
const ( const (
DefaultCodespace sdk.CodespaceType = 6
// Cool module reserves error 400-499 lawl // Cool module reserves error 400-499 lawl
CodeIncorrectCoolAnswer sdk.CodeType = 400 CodeIncorrectCoolAnswer sdk.CodeType = 400
) )
// ErrIncorrectCoolAnswer - Error returned upon an incorrect guess // ErrIncorrectCoolAnswer - Error returned upon an incorrect guess
func ErrIncorrectCoolAnswer(answer string) sdk.Error { func ErrIncorrectCoolAnswer(codespace sdk.CodespaceType, answer string) sdk.Error {
return sdk.NewError(CodeIncorrectCoolAnswer, fmt.Sprintf("Incorrect cool answer: %v", answer)) 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 { func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) { switch msg := msg.(type) {
case SetTrendMsg: case MsgSetTrend:
return handleSetTrendMsg(ctx, k, msg) return handleMsgSetTrend(ctx, k, msg)
case QuizMsg: case MsgQuiz:
return handleQuizMsg(ctx, k, msg) return handleMsgQuiz(ctx, k, msg)
default: default:
errMsg := fmt.Sprintf("Unrecognized cool Msg type: %v", reflect.TypeOf(msg).Name()) errMsg := fmt.Sprintf("Unrecognized cool Msg type: %v", reflect.TypeOf(msg).Name())
return sdk.ErrUnknownRequest(errMsg).Result() return sdk.ErrUnknownRequest(errMsg).Result()
@ -32,19 +32,19 @@ func NewHandler(k Keeper) sdk.Handler {
} }
} }
// Handle QuizMsg This is the engine of your module // Handle MsgQuiz This is the engine of your module
func handleSetTrendMsg(ctx sdk.Context, k Keeper, msg SetTrendMsg) sdk.Result { func handleMsgSetTrend(ctx sdk.Context, k Keeper, msg MsgSetTrend) sdk.Result {
k.setTrend(ctx, msg.Cool) k.setTrend(ctx, msg.Cool)
return sdk.Result{} return sdk.Result{}
} }
// Handle QuizMsg This is the engine of your module // Handle MsgQuiz This is the engine of your module
func handleQuizMsg(ctx sdk.Context, k Keeper, msg QuizMsg) sdk.Result { func handleMsgQuiz(ctx sdk.Context, k Keeper, msg MsgQuiz) sdk.Result {
correct := k.CheckTrend(ctx, msg.CoolAnswer) correct := k.CheckTrend(ctx, msg.CoolAnswer)
if !correct { if !correct {
return ErrIncorrectCoolAnswer(msg.CoolAnswer).Result() return ErrIncorrectCoolAnswer(k.codespace, msg.CoolAnswer).Result()
} }
if ctx.IsCheckTx() { if ctx.IsCheckTx() {

View File

@ -7,14 +7,16 @@ import (
// Keeper - handlers sets/gets of custom variables for your module // Keeper - handlers sets/gets of custom variables for your module
type Keeper struct { type Keeper struct {
ck bank.CoinKeeper ck bank.Keeper
storeKey sdk.StoreKey // The (unexposed) key used to access the store from the Context. storeKey sdk.StoreKey // The (unexposed) key used to access the store from the Context.
codespace sdk.CodespaceType
} }
// NewKeeper - Returns the Keeper // NewKeeper - Returns the Keeper
func NewKeeper(key sdk.StoreKey, bankKeeper bank.CoinKeeper) Keeper { func NewKeeper(key sdk.StoreKey, bankKeeper bank.Keeper, codespace sdk.CodespaceType) Keeper {
return Keeper{bankKeeper, key} return Keeper{bankKeeper, key, codespace}
} }
// Key to knowing the trend on the streets! // 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 // 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) k.setTrend(ctx, data.Trend)
return nil 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" 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 // custom to your message
type SetTrendMsg struct { type MsgSetTrend struct {
Sender sdk.Address Sender sdk.Address
Cool string Cool string
} }
// Genesis state - specify genesis trend // genesis state - specify genesis trend
type CoolGenesis struct { type Genesis struct {
Trend string `json:"trend"` Trend string `json:"trend"`
} }
// New cool message // new cool message
func NewSetTrendMsg(sender sdk.Address, cool string) SetTrendMsg { func NewMsgSetTrend(sender sdk.Address, cool string) MsgSetTrend {
return SetTrendMsg{ return MsgSetTrend{
Sender: sender, Sender: sender,
Cool: cool, Cool: cool,
} }
} }
// enforce the msg type at compile time // enforce the msg type at compile time
var _ sdk.Msg = SetTrendMsg{} var _ sdk.Msg = MsgSetTrend{}
// nolint // nolint
func (msg SetTrendMsg) Type() string { return "cool" } func (msg MsgSetTrend) Type() string { return "cool" }
func (msg SetTrendMsg) Get(key interface{}) (value interface{}) { return nil } func (msg MsgSetTrend) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
func (msg SetTrendMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} } func (msg MsgSetTrend) String() string {
func (msg SetTrendMsg) String() string { return fmt.Sprintf("MsgSetTrend{Sender: %v, Cool: %v}", msg.Sender, msg.Cool)
return fmt.Sprintf("SetTrendMsg{Sender: %v, Cool: %v}", msg.Sender, msg.Cool)
} }
// Validate Basic is used to quickly disqualify obviously invalid messages quickly // 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 { if len(msg.Sender) == 0 {
return sdk.ErrUnknownAddress(msg.Sender.String()).Trace("") 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 // 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) b, err := json.Marshal(msg)
if err != nil { if err != nil {
panic(err) 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 // A message type to quiz how cool you are. these fields are can be entirely
// arbitrary and custom to your message // arbitrary and custom to your message
type QuizMsg struct { type MsgQuiz struct {
Sender sdk.Address Sender sdk.Address
CoolAnswer string CoolAnswer string
} }
// New cool message // New cool message
func NewQuizMsg(sender sdk.Address, coolerthancool string) QuizMsg { func NewMsgQuiz(sender sdk.Address, coolerthancool string) MsgQuiz {
return QuizMsg{ return MsgQuiz{
Sender: sender, Sender: sender,
CoolAnswer: coolerthancool, CoolAnswer: coolerthancool,
} }
} }
// enforce the msg type at compile time // enforce the msg type at compile time
var _ sdk.Msg = QuizMsg{} var _ sdk.Msg = MsgQuiz{}
// nolint // nolint
func (msg QuizMsg) Type() string { return "cool" } func (msg MsgQuiz) Type() string { return "cool" }
func (msg QuizMsg) Get(key interface{}) (value interface{}) { return nil } func (msg MsgQuiz) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
func (msg QuizMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} } func (msg MsgQuiz) String() string {
func (msg QuizMsg) String() string { return fmt.Sprintf("MsgQuiz{Sender: %v, CoolAnswer: %v}", msg.Sender, msg.CoolAnswer)
return fmt.Sprintf("QuizMsg{Sender: %v, CoolAnswer: %v}", msg.Sender, msg.CoolAnswer)
} }
// Validate Basic is used to quickly disqualify obviously invalid messages quickly // 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 { if len(msg.Sender) == 0 {
return sdk.ErrUnknownAddress(msg.Sender.String()).Trace("") 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 // 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) b, err := json.Marshal(msg)
if err != nil { if err != nil {
panic(err) 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 ( import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow" "github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
"github.com/cosmos/cosmos-sdk/wire" "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 { func MineCmd(cdc *wire.Codec) *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: "mine [difficulty] [count] [nonce] [solution]", Use: "mine [difficulty] [count] [nonce] [solution]",
Short: "Mine some coins with proof-of-work!", Short: "Mine some coins with proof-of-work!",
Args: cobra.ExactArgs(4),
RunE: func(cmd *cobra.Command, args []string) error { 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)) ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
from, err := ctx.GetFromAddress() from, err := ctx.GetFromAddress()
@ -36,12 +31,10 @@ func MineCmd(cdc *wire.Codec) *cobra.Command {
if err != nil { if err != nil {
return err return err
} }
count, err := strconv.ParseUint(args[1], 0, 64) count, err := strconv.ParseUint(args[1], 0, 64)
if err != nil { if err != nil {
return err return err
} }
nonce, err := strconv.ParseUint(args[2], 0, 64) nonce, err := strconv.ParseUint(args[2], 0, 64)
if err != nil { if err != nil {
return err return err
@ -49,19 +42,13 @@ func MineCmd(cdc *wire.Codec) *cobra.Command {
solution := []byte(args[3]) solution := []byte(args[3])
msg := pow.NewMineMsg(from, difficulty, count, nonce, solution) msg := pow.NewMsgMine(from, difficulty, count, nonce, solution)
// get account name // get account name
name := ctx.FromAddressName 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 // 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 { if err != nil {
return err return err
} }

View File

@ -4,9 +4,12 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
// TODO remove, seems hacky
type CodeType = sdk.CodeType type CodeType = sdk.CodeType
// POW errors reserve 200 ~ 299
const ( const (
DefaultCodespace sdk.CodespaceType = 5
CodeInvalidDifficulty CodeType = 201 CodeInvalidDifficulty CodeType = 201
CodeNonexistentDifficulty CodeType = 202 CodeNonexistentDifficulty CodeType = 202
CodeNonexistentReward CodeType = 203 CodeNonexistentReward CodeType = 203
@ -40,43 +43,37 @@ func codeToDefaultMsg(code CodeType) string {
} }
} }
func ErrInvalidDifficulty(msg string) sdk.Error { // nolint
return newError(CodeInvalidDifficulty, msg) func ErrInvalidDifficulty(codespace sdk.CodespaceType, msg string) sdk.Error {
return newError(codespace, CodeInvalidDifficulty, msg)
} }
func ErrNonexistentDifficulty(codespace sdk.CodespaceType) sdk.Error {
func ErrNonexistentDifficulty() sdk.Error { return newError(codespace, CodeNonexistentDifficulty, "")
return newError(CodeNonexistentDifficulty, "")
} }
func ErrNonexistentReward(codespace sdk.CodespaceType) sdk.Error {
func ErrNonexistentReward() sdk.Error { return newError(codespace, CodeNonexistentReward, "")
return newError(CodeNonexistentReward, "")
} }
func ErrNonexistentCount(codespace sdk.CodespaceType) sdk.Error {
func ErrNonexistentCount() sdk.Error { return newError(codespace, CodeNonexistentCount, "")
return newError(CodeNonexistentCount, "")
} }
func ErrInvalidProof(codespace sdk.CodespaceType, msg string) sdk.Error {
func ErrInvalidProof(msg string) sdk.Error { return newError(codespace, CodeInvalidProof, msg)
return newError(CodeInvalidProof, msg)
} }
func ErrNotBelowTarget(codespace sdk.CodespaceType, msg string) sdk.Error {
func ErrNotBelowTarget(msg string) sdk.Error { return newError(codespace, CodeNotBelowTarget, msg)
return newError(CodeNotBelowTarget, msg)
} }
func ErrInvalidCount(codespace sdk.CodespaceType, msg string) sdk.Error {
func ErrInvalidCount(msg string) sdk.Error { return newError(codespace, CodeInvalidCount, msg)
return newError(CodeInvalidCount, msg)
} }
func msgOrDefaultMsg(msg string, code CodeType) string { func msgOrDefaultMsg(msg string, code CodeType) string {
if msg != "" { if msg != "" {
return 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) 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" sdk "github.com/cosmos/cosmos-sdk/types"
) )
// POW handler
func (pk Keeper) Handler(ctx sdk.Context, msg sdk.Msg) sdk.Result { func (pk Keeper) Handler(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) { switch msg := msg.(type) {
case MineMsg: case MsgMine:
return handleMineMsg(ctx, pk, msg) return handleMsgMine(ctx, pk, msg)
default: default:
errMsg := "Unrecognized pow Msg type: " + reflect.TypeOf(msg).Name() errMsg := "Unrecognized pow Msg type: " + reflect.TypeOf(msg).Name()
return sdk.ErrUnknownRequest(errMsg).Result() 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 // precondition: msg has passed ValidateBasic

View File

@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
wire "github.com/cosmos/cosmos-sdk/wire" wire "github.com/cosmos/cosmos-sdk/wire"
@ -19,10 +20,10 @@ func TestPowHandler(t *testing.T) {
auth.RegisterBaseAccount(cdc) auth.RegisterBaseAccount(cdc)
am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{}) am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{})
ctx := sdk.NewContext(ms, abci.Header{}, false, nil) ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
config := NewPowConfig("pow", int64(1)) config := NewConfig("pow", int64(1))
ck := bank.NewCoinKeeper(am) ck := bank.NewKeeper(am)
keeper := NewKeeper(capKey, config, ck) keeper := NewKeeper(capKey, config, ck, DefaultCodespace)
handler := keeper.Handler handler := keeper.Handler
@ -30,11 +31,11 @@ func TestPowHandler(t *testing.T) {
count := uint64(1) count := uint64(1)
difficulty := uint64(2) 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) assert.Nil(t, err)
nonce, proof := mine(addr, count, difficulty) nonce, proof := mine(addr, count, difficulty)
msg := NewMineMsg(addr, difficulty, count, nonce, proof) msg := NewMsgMine(addr, difficulty, count, nonce, proof)
result := handler(ctx, msg) result := handler(ctx, msg)
assert.Equal(t, result, sdk.Result{}) assert.Equal(t, result, sdk.Result{})
@ -51,7 +52,7 @@ func TestPowHandler(t *testing.T) {
difficulty = uint64(4) difficulty = uint64(4)
nonce, proof = mine(addr, count, difficulty) nonce, proof = mine(addr, count, difficulty)
msg = NewMineMsg(addr, difficulty, count, nonce, proof) msg = NewMsgMine(addr, difficulty, count, nonce, proof)
result = handler(ctx, msg) result = handler(ctx, msg)
assert.NotEqual(t, result, sdk.Result{}) 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 // module users must specify coin denomination and reward (constant) per PoW solution
type PowConfig struct { type Config struct {
Denomination string Denomination string
Reward int64 Reward int64
} }
// genesis info must specify starting difficulty and starting count // genesis info must specify starting difficulty and starting count
type PowGenesis struct { type Genesis struct {
Difficulty uint64 `json:"difficulty"` Difficulty uint64 `json:"difficulty"`
Count uint64 `json:"count"` Count uint64 `json:"count"`
} }
// POW Keeper
type Keeper struct { type Keeper struct {
key sdk.StoreKey key sdk.StoreKey
config PowConfig config Config
ck bank.CoinKeeper ck bank.Keeper
codespace sdk.CodespaceType
} }
func NewPowConfig(denomination string, reward int64) PowConfig { func NewConfig(denomination string, reward int64) Config {
return PowConfig{denomination, reward} return Config{denomination, reward}
} }
func NewKeeper(key sdk.StoreKey, config PowConfig, ck bank.CoinKeeper) Keeper { func NewKeeper(key sdk.StoreKey, config Config, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
return Keeper{key, config, ck} return Keeper{key, config, ck, codespace}
} }
func (pk Keeper) InitGenesis(ctx sdk.Context, genesis PowGenesis) error { // InitGenesis for the POW module
pk.SetLastDifficulty(ctx, genesis.Difficulty) func InitGenesis(ctx sdk.Context, k Keeper, genesis Genesis) error {
pk.SetLastCount(ctx, genesis.Count) k.SetLastDifficulty(ctx, genesis.Difficulty)
k.SetLastCount(ctx, genesis.Count)
return nil 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") var lastDifficultyKey = []byte("lastDifficultyKey")
func (pk Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) { // get the last mining difficulty
store := ctx.KVStore(pk.key) func (k Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
store := ctx.KVStore(k.key)
stored := store.Get(lastDifficultyKey) stored := store.Get(lastDifficultyKey)
if stored == nil { if stored == nil {
panic("no stored difficulty") 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) { // set the last mining difficulty
store := ctx.KVStore(pk.key) func (k Keeper) SetLastDifficulty(ctx sdk.Context, diff uint64) {
store := ctx.KVStore(k.key)
store.Set(lastDifficultyKey, []byte(strconv.FormatUint(diff, 16))) store.Set(lastDifficultyKey, []byte(strconv.FormatUint(diff, 16)))
} }
var countKey = []byte("count") var countKey = []byte("count")
func (pk Keeper) GetLastCount(ctx sdk.Context) (uint64, error) { // get the last count
store := ctx.KVStore(pk.key) func (k Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
store := ctx.KVStore(k.key)
stored := store.Get(countKey) stored := store.Get(countKey)
if stored == nil { if stored == nil {
panic("no stored count") 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) { // set the last count
store := ctx.KVStore(pk.key) func (k Keeper) SetLastCount(ctx sdk.Context, count uint64) {
store := ctx.KVStore(k.key)
store.Set(countKey, []byte(strconv.FormatUint(count, 16))) 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 { if err != nil {
return 0, 0, ErrNonexistentDifficulty() return 0, 0, ErrNonexistentDifficulty(k.codespace)
} }
newDifficulty := lastDifficulty + 1 newDifficulty := lastDifficulty + 1
lastCount, err := pk.GetLastCount(ctx) lastCount, err := k.GetLastCount(ctx)
if err != nil { if err != nil {
return 0, 0, ErrNonexistentCount() return 0, 0, ErrNonexistentCount(k.codespace)
} }
newCount := lastCount + 1 newCount := lastCount + 1
if count != newCount { 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 { 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 return newDifficulty, newCount, nil
} }
func (pk Keeper) ApplyValid(ctx sdk.Context, sender sdk.Address, newDifficulty uint64, newCount uint64) sdk.Error { // Add some coins for a POW well done
_, ckErr := pk.ck.AddCoins(ctx, sender, []sdk.Coin{sdk.Coin{pk.config.Denomination, pk.config.Reward}}) 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 { if ckErr != nil {
return ckErr return ckErr
} }
pk.SetLastDifficulty(ctx, newDifficulty) k.SetLastDifficulty(ctx, newDifficulty)
pk.SetLastCount(ctx, newCount) k.SetLastCount(ctx, newCount)
return nil return nil
} }

View File

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

View File

@ -9,9 +9,10 @@ import (
crypto "github.com/tendermint/go-crypto" 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) 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 { func hash(sender sdk.Address, count uint64, nonce uint64) []byte {

View File

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

View File

@ -9,70 +9,65 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
func TestNewMineMsg(t *testing.T) { func TestNewMsgMine(t *testing.T) {
addr := sdk.Address([]byte("sender")) addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 0, 0, 0, []byte("")} msg := MsgMine{addr, 0, 0, 0, []byte("")}
equiv := NewMineMsg(addr, 0, 0, 0, []byte("")) equiv := NewMsgMine(addr, 0, 0, 0, []byte(""))
assert.Equal(t, msg, equiv, "%s != %s", msg, equiv) assert.Equal(t, msg, equiv, "%s != %s", msg, equiv)
} }
func TestMineMsgType(t *testing.T) { func TestMsgMineType(t *testing.T) {
addr := sdk.Address([]byte("sender")) 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") assert.Equal(t, msg.Type(), "pow")
} }
func TestMineMsgValidation(t *testing.T) { func TestMsgMineValidation(t *testing.T) {
addr := sdk.Address([]byte("sender")) addr := sdk.Address([]byte("sender"))
otherAddr := sdk.Address([]byte("another")) otherAddr := sdk.Address([]byte("another"))
count := uint64(0) count := uint64(0)
for difficulty := uint64(1); difficulty < 1000; difficulty += 100 { for difficulty := uint64(1); difficulty < 1000; difficulty += 100 {
count += 1
count++
nonce, proof := mine(addr, count, difficulty) nonce, proof := mine(addr, count, difficulty)
msg := MineMsg{addr, difficulty, count, nonce, proof} msg := MsgMine{addr, difficulty, count, nonce, proof}
err := msg.ValidateBasic() err := msg.ValidateBasic()
assert.Nil(t, err, "error with difficulty %d - %+v", difficulty, err) assert.Nil(t, err, "error with difficulty %d - %+v", difficulty, err)
msg.Count += 1 msg.Count++
err = msg.ValidateBasic() err = msg.ValidateBasic()
assert.NotNil(t, err, "count was wrong, should have thrown error with msg %s", msg) assert.NotNil(t, err, "count was wrong, should have thrown error with msg %s", msg)
msg.Count -= 1 msg.Count--
msg.Nonce += 1 msg.Nonce++
err = msg.ValidateBasic() err = msg.ValidateBasic()
assert.NotNil(t, err, "nonce was wrong, should have thrown error with msg %s", msg) assert.NotNil(t, err, "nonce was wrong, should have thrown error with msg %s", msg)
msg.Nonce -= 1 msg.Nonce--
msg.Sender = otherAddr msg.Sender = otherAddr
err = msg.ValidateBasic() err = msg.ValidateBasic()
assert.NotNil(t, err, "sender was wrong, should have thrown error with msg %s", msg) 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")) addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 0, 0, 0, []byte("abc")} msg := MsgMine{addr, 0, 0, 0, []byte("abc")}
res := msg.String() 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")) addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 0, 0, 0, []byte("")} msg := MsgMine{addr, 1, 1, 1, []byte("abc")}
res := msg.Get(nil)
assert.Nil(t, res)
}
func TestMineMsgGetSignBytes(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 1, 1, 1, []byte("abc")}
res := msg.GetSignBytes() res := msg.GetSignBytes()
assert.Equal(t, string(res), `{"sender":"73656E646572","difficulty":1,"count":1,"nonce":1,"proof":"YWJj"}`) 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")) addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 1, 1, 1, []byte("abc")} msg := MsgMine{addr, 1, 1, 1, []byte("abc")}
res := msg.GetSigners() res := msg.GetSigners()
assert.Equal(t, fmt.Sprintf("%v", res), "[73656E646572]") 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 { func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) { switch msg := msg.(type) {
case BondMsg: case MsgBond:
return handleBondMsg(ctx, k, msg) return handleMsgBond(ctx, k, msg)
case UnbondMsg: case MsgUnbond:
return handleUnbondMsg(ctx, k, msg) return handleMsgUnbond(ctx, k, msg)
default: default:
return sdk.ErrUnknownRequest("No match for message type.").Result() 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) power, err := k.Bond(ctx, msg.Address, msg.PubKey, msg.Stake)
if err != nil { if err != nil {
return err.Result() return err.Result()
@ -32,12 +32,12 @@ func handleBondMsg(ctx sdk.Context, k Keeper, msg BondMsg) sdk.Result {
} }
return sdk.Result{ return sdk.Result{
Code: sdk.CodeOK, Code: sdk.ABCICodeOK,
ValidatorUpdates: abci.Validators{valSet}, 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) pubKey, _, err := k.Unbond(ctx, msg.Address)
if err != nil { if err != nil {
return err.Result() return err.Result()
@ -49,7 +49,7 @@ func handleUnbondMsg(ctx sdk.Context, k Keeper, msg UnbondMsg) sdk.Result {
} }
return sdk.Result{ return sdk.Result{
Code: sdk.CodeOK, Code: sdk.ABCICodeOK,
ValidatorUpdates: abci.Validators{valSet}, ValidatorUpdates: abci.Validators{valSet},
} }
} }

View File

@ -12,20 +12,23 @@ const stakingToken = "steak"
const moduleName = "simplestake" const moduleName = "simplestake"
// simple stake keeper
type Keeper struct { type Keeper struct {
ck bank.CoinKeeper ck bank.Keeper
key sdk.StoreKey key sdk.StoreKey
cdc *wire.Codec 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() cdc := wire.NewCodec()
wire.RegisterCrypto(cdc) wire.RegisterCrypto(cdc)
return Keeper{ return Keeper{
key: key, key: key,
cdc: cdc, cdc: cdc,
ck: coinKeeper, ck: coinKeeper,
codespace: codespace,
} }
} }
@ -57,9 +60,10 @@ func (k Keeper) deleteBondInfo(ctx sdk.Context, addr sdk.Address) {
store.Delete(addr) 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) { func (k Keeper) Bond(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, stake sdk.Coin) (int64, sdk.Error) {
if stake.Denom != stakingToken { if stake.Denom != stakingToken {
return 0, ErrIncorrectStakingToken() return 0, ErrIncorrectStakingToken(k.codespace)
} }
_, err := k.ck.SubtractCoins(ctx, addr, []sdk.Coin{stake}) _, 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 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) { func (k Keeper) Unbond(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64, sdk.Error) {
bi := k.getBondInfo(ctx, addr) bi := k.getBondInfo(ctx, addr)
if bi.isEmpty() { if bi.isEmpty() {
return nil, 0, ErrInvalidUnbond() return nil, 0, ErrInvalidUnbond(k.codespace)
} }
k.deleteBondInfo(ctx, addr) 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) { func (k Keeper) bondWithoutCoins(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, stake sdk.Coin) (int64, sdk.Error) {
if stake.Denom != stakingToken { if stake.Denom != stakingToken {
return 0, ErrIncorrectStakingToken() return 0, ErrIncorrectStakingToken(k.codespace)
} }
bi := k.getBondInfo(ctx, addr) 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) { func (k Keeper) unbondWithoutCoins(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64, sdk.Error) {
bi := k.getBondInfo(ctx, addr) bi := k.getBondInfo(ctx, addr)
if bi.isEmpty() { if bi.isEmpty() {
return nil, 0, ErrInvalidUnbond() return nil, 0, ErrInvalidUnbond(k.codespace)
} }
k.deleteBondInfo(ctx, addr) k.deleteBondInfo(ctx, addr)

View File

@ -10,6 +10,7 @@ import (
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
@ -32,8 +33,8 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) {
func TestKeeperGetSet(t *testing.T) { func TestKeeperGetSet(t *testing.T) {
ms, _, capKey := setupMultiStore() ms, _, capKey := setupMultiStore()
ctx := sdk.NewContext(ms, abci.Header{}, false, nil) ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
stakeKeeper := NewKeeper(capKey, bank.NewCoinKeeper(nil)) stakeKeeper := NewKeeper(capKey, bank.NewKeeper(nil), DefaultCodespace)
addr := sdk.Address([]byte("some-address")) addr := sdk.Address([]byte("some-address"))
bi := stakeKeeper.getBondInfo(ctx, addr) bi := stakeKeeper.getBondInfo(ctx, addr)
@ -59,17 +60,17 @@ func TestBonding(t *testing.T) {
cdc := wire.NewCodec() cdc := wire.NewCodec()
auth.RegisterBaseAccount(cdc) 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{}) accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{})
coinKeeper := bank.NewCoinKeeper(accountMapper) coinKeeper := bank.NewKeeper(accountMapper)
stakeKeeper := NewKeeper(capKey, coinKeeper) stakeKeeper := NewKeeper(capKey, coinKeeper, DefaultCodespace)
addr := sdk.Address([]byte("some-address")) addr := sdk.Address([]byte("some-address"))
privKey := crypto.GenPrivKeyEd25519() privKey := crypto.GenPrivKeyEd25519()
pubKey := privKey.PubKey() pubKey := privKey.PubKey()
_, _, err := stakeKeeper.unbondWithoutCoins(ctx, addr) _, _, 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}) _, err = stakeKeeper.bondWithoutCoins(ctx, addr, pubKey, sdk.Coin{"steak", 10})
assert.Nil(t, err) assert.Nil(t, err)
@ -82,5 +83,5 @@ func TestBonding(t *testing.T) {
assert.Equal(t, pubKey, pk) assert.Equal(t, pubKey, pk)
_, _, err = stakeKeeper.unbondWithoutCoins(ctx, addr) _, _, 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() privKey := crypto.GenPrivKeyEd25519()
cases := []struct { cases := []struct {
valid bool valid bool
bondMsg BondMsg msgBond MsgBond
}{ }{
{true, NewBondMsg(sdk.Address{}, sdk.Coin{"mycoin", 5}, privKey.PubKey())}, {true, NewMsgBond(sdk.Address{}, sdk.Coin{"mycoin", 5}, privKey.PubKey())},
{false, NewBondMsg(sdk.Address{}, sdk.Coin{"mycoin", 0}, privKey.PubKey())}, {false, NewMsgBond(sdk.Address{}, sdk.Coin{"mycoin", 0}, privKey.PubKey())},
} }
for i, tc := range cases { for i, tc := range cases {
err := tc.bondMsg.ValidateBasic() err := tc.msgBond.ValidateBasic()
if tc.valid { if tc.valid {
assert.Nil(t, err, "%d: %+v", i, err) assert.Nil(t, err, "%d: %+v", i, err)
} else { } 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") var capKeyMainStore = sdk.NewKVStoreKey("main")
// Create BaseApp. // Create BaseApp.
var baseApp = bam.NewBaseApp("kvstore", logger, db) var baseApp = bam.NewBaseApp("kvstore", nil, logger, db)
// Set mounts for BaseApp's MultiStore. // Set mounts for BaseApp's MultiStore.
baseApp.MountStoresIAVL(capKeyMainStore) baseApp.MountStoresIAVL(capKeyMainStore)
@ -41,7 +41,7 @@ func main() {
baseApp.SetTxDecoder(decodeTx) baseApp.SetTxDecoder(decodeTx)
// Set a handler Route. // Set a handler Route.
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore)) baseApp.Router().AddRoute("kvstore", Handler(capKeyMainStore))
// Load latest version. // Load latest version.
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil { if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
@ -65,11 +65,12 @@ func main() {
return 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 { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
dTx, ok := msg.(kvstoreTx) dTx, ok := msg.(kvstoreTx)
if !ok { if !ok {
panic("KVStoreHandler should only receive kvstoreTx") panic("Handler should only receive kvstoreTx")
} }
// tx is already unmarshalled // tx is already unmarshalled

View File

@ -13,19 +13,6 @@ type kvstoreTx struct {
bytes []byte 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 { func (tx kvstoreTx) Type() string {
return "kvstore" return "kvstore"
} }

View File

@ -6,16 +6,18 @@ import (
"path/filepath" "path/filepath"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
bam "github.com/cosmos/cosmos-sdk/baseapp" bam "github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
) )
// NewApp creates a simple mock kvstore app for testing. // NewApp creates a simple mock kvstore app for testing. It should work
// It should work similar to a real app. // similar to a real app. Make sure rootDir is empty before running the test,
// Make sure rootDir is empty before running the test,
// in order to guarantee consistent results // in order to guarantee consistent results
func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { func NewApp(rootDir string, logger log.Logger) (abci.Application, error) {
db, err := dbm.NewGoLevelDB("mock", filepath.Join(rootDir, "data")) 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") capKeyMainStore := sdk.NewKVStoreKey("main")
// Create BaseApp. // Create BaseApp.
baseApp := bam.NewBaseApp("kvstore", logger, db) baseApp := bam.NewBaseApp("kvstore", nil, logger, db)
// Set mounts for BaseApp's MultiStore. // Set mounts for BaseApp's MultiStore.
baseApp.MountStoresIAVL(capKeyMainStore) baseApp.MountStoresIAVL(capKeyMainStore)
@ -103,11 +105,10 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci
} }
} }
// GenInitOptions can be passed into InitCmd, // AppGenState can be passed into InitCmd, returns a static string of a few
// returns a static string of a few key-values that can be parsed // key-values that can be parsed by InitChainer
// by InitChainer func AppGenState(_ *wire.Codec, _ []json.RawMessage) (appState json.RawMessage, err error) {
func GenInitOptions(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) { appState = json.RawMessage(`{
opts := []byte(`{
"values": [ "values": [
{ {
"key": "hello", "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) require.NoError(t, err)
// initialize it future-way // initialize it future-way
opts, err := GenInitOptions(nil, nil, "") appState, err := AppGenState(nil, nil)
require.NoError(t, err) 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.InitChain(req)
app.Commit() 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 { func (tx kvstoreTx) Type() string {
return "kvstore" return "kvstore"
} }

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