Merge branch 'develop' into feature/ibc
This commit is contained in:
commit
ffed398035
|
@ -27,11 +27,17 @@ jobs:
|
|||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make get_vendor_deps
|
||||
- run:
|
||||
name: linter
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
go get -u github.com/tendermint/lint/golint
|
||||
go get -u github.com/alecthomas/gometalinter
|
||||
- run:
|
||||
name: binaries
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make build
|
||||
make install
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
|
@ -46,6 +52,54 @@ jobs:
|
|||
paths:
|
||||
- /go/src/github.com/cosmos/cosmos-sdk
|
||||
|
||||
lint:
|
||||
<<: *defaults
|
||||
parallelism: 4
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v1-pkg-cache
|
||||
- restore_cache:
|
||||
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: Lint source
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
gometalinter --disable-all --enable='golint' --vendor ./...
|
||||
|
||||
test_unit:
|
||||
<<: *defaults
|
||||
parallelism: 4
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v1-pkg-cache
|
||||
- restore_cache:
|
||||
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: Test unit
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make test_unit
|
||||
|
||||
test_cli:
|
||||
<<: *defaults
|
||||
parallelism: 1
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v1-pkg-cache
|
||||
- restore_cache:
|
||||
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: Test cli
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make test_cli
|
||||
|
||||
test_cover:
|
||||
<<: *defaults
|
||||
parallelism: 4
|
||||
|
@ -59,6 +113,8 @@ jobs:
|
|||
- run:
|
||||
name: Run tests
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make install
|
||||
for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v /vendor/ | circleci tests split --split-by=timings); do
|
||||
id=$(basename "$pkg")
|
||||
|
||||
|
@ -94,10 +150,18 @@ workflows:
|
|||
test-suite:
|
||||
jobs:
|
||||
- setup_dependencies
|
||||
- lint:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- test_unit:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- test_cli:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- test_cover:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- upload_coverage:
|
||||
requires:
|
||||
- test_cover
|
||||
|
||||
|
|
55
CHANGELOG.md
55
CHANGELOG.md
|
@ -1,12 +1,50 @@
|
|||
# Changelog
|
||||
|
||||
## 0.15.0 (TBD)
|
||||
## 0.16.0 (TBD)
|
||||
|
||||
BREAKING CHANGES
|
||||
|
||||
* Move module REST/CLI packages to x/[module]/client/rest and x/[module]/client/cli
|
||||
* Gaia simple-staking bond and unbond functions replaced
|
||||
* [stake] Delegator bonds now store the height at which they were updated
|
||||
* All module keepers now require a codespace, see basecoin or democoin for usage
|
||||
* Many changes to names throughout
|
||||
* Type as a prefix naming convention applied (ex. BondMsg -> MsgBond)
|
||||
* Removed redundancy in names (ex. stake.StakeKeeper -> stake.Keeper)
|
||||
* Removed SealedAccountMapper
|
||||
* gaiad init now requires use of `--name` flag
|
||||
* Removed Get from Msg interface
|
||||
* types/rational now extends big.Rat
|
||||
|
||||
FEATURES:
|
||||
|
||||
* Add CacheContext
|
||||
* Add auto sequencing to client
|
||||
* Add FeeHandler to ante handler
|
||||
* Gaia stake commands include, DeclareCandidacy, EditCandidacy, Delegate, Unbond
|
||||
* MountStoreWithDB without providing a custom store works.
|
||||
* Repo is now lint compliant / GoMetaLinter with tendermint-lint integrated into CI
|
||||
* Better key output, pubkey go-amino hex bytes now output by default
|
||||
* gaiad init overhaul
|
||||
* Create genesis transactions with `gaiad init gen-tx`
|
||||
* New genesis account keys are automatically added to the client keybase (introduce `--client-home` flag)
|
||||
* Initialize with genesis txs using `--gen-txs` flag
|
||||
* Context now has access to the application-configured logger
|
||||
|
||||
BUG FIXES
|
||||
* Gaia now uses stake, ported from github.com/cosmos/gaia
|
||||
* Auto-sequencing now works correctly
|
||||
|
||||
## 0.15.1 (April 29, 2018)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* Update Tendermint to v0.19.1 (includes many rpc fixes)
|
||||
|
||||
## 0.15.0 (April 29, 2018)
|
||||
|
||||
NOTE: v0.15.0 is a large breaking change that updates the encoding scheme to use
|
||||
[Amino](github.com/tendermint/go-amino).
|
||||
|
||||
For details on how this changes encoding for public keys and addresses,
|
||||
see the [docs](https://github.com/tendermint/tendermint/blob/v0.19.1/docs/specification/new-spec/encoding.md#public-key-cryptography).
|
||||
|
||||
BREAKING CHANGES
|
||||
|
||||
|
@ -14,8 +52,13 @@ BREAKING CHANGES
|
|||
* [store] Add `SubspaceIterator` and `ReverseSubspaceIterator` to `KVStore` interface
|
||||
* [basecoin] NewBasecoinApp takes a `dbm.DB` and uses namespaced DBs for substores
|
||||
|
||||
BUG FIXES
|
||||
FEATURES:
|
||||
|
||||
* Add CacheContext
|
||||
* Add auto sequencing to client
|
||||
* Add FeeHandler to ante handler
|
||||
|
||||
BUG FIXES
|
||||
* MountStoreWithDB without providing a custom store works.
|
||||
|
||||
## 0.14.1 (April 9, 2018)
|
||||
|
@ -63,7 +106,7 @@ BREAKING CHANGES
|
|||
to allow mounting multiple stores with their own DB until they can share one
|
||||
* [x/staking] Renamed to `simplestake`
|
||||
* [builder] Functions don't take `passphrase` as argument
|
||||
* [server] GenAppState returns generated seed and address
|
||||
* [server] GenAppParams returns generated seed and address
|
||||
* [basecoind] `init` command outputs JSON of everything necessary for testnet
|
||||
* [basecoind] `basecoin.db -> data/basecoin.db`
|
||||
* [basecli] `data/keys.db -> keys/keys.db`
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
branch = "master"
|
||||
name = "github.com/btcsuite/btcd"
|
||||
packages = ["btcec"]
|
||||
revision = "2be2f12b358dc57d70b8f501b00be450192efbc3"
|
||||
revision = "675abc5df3c5531bc741b56a765e35623459da6d"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
|
@ -277,8 +277,8 @@
|
|||
[[projects]]
|
||||
name = "github.com/tendermint/go-amino"
|
||||
packages = ["."]
|
||||
revision = "42246108ff925a457fb709475070a03dfd3e2b5c"
|
||||
version = "0.9.6"
|
||||
revision = "ed62928576cfcaf887209dc96142cd79cdfff389"
|
||||
version = "0.9.9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/tendermint/go-crypto"
|
||||
|
@ -342,8 +342,8 @@
|
|||
"types/priv_validator",
|
||||
"version"
|
||||
]
|
||||
revision = "d0beaba7e8a5652506a34b5fab299cc2dc274c02"
|
||||
version = "v0.19.0"
|
||||
revision = "26f633ed48441f72895b710f0e87b7b6c6791066"
|
||||
version = "v0.19.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/tendermint/tmlibs"
|
||||
|
@ -360,8 +360,8 @@
|
|||
"pubsub",
|
||||
"pubsub/query"
|
||||
]
|
||||
revision = "737154202faf75c70437f59ba5303f2eb09f5636"
|
||||
version = "0.8.2-rc1"
|
||||
revision = "d94e312673e16a11ea55d742cefb3e331228f898"
|
||||
version = "v0.8.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -377,13 +377,14 @@
|
|||
"ripemd160",
|
||||
"salsa20/salsa"
|
||||
]
|
||||
revision = "d6449816ce06963d9d136eee5a56fca5b0616e7e"
|
||||
revision = "b49d69b5da943f7ef3c9cf91c8777c1f78a0cc3c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
"http/httpguts",
|
||||
"http2",
|
||||
"http2/hpack",
|
||||
"idna",
|
||||
|
@ -391,13 +392,13 @@
|
|||
"lex/httplex",
|
||||
"trace"
|
||||
]
|
||||
revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
|
||||
revision = "5f9ae10d9af5b1c89ae6904293b14b064d4ada23"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
|
||||
revision = "cbbc999da32df943dac6cd71eb3ee39e1d7838b9"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/text"
|
||||
|
@ -424,7 +425,7 @@
|
|||
branch = "master"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
revision = "51d0944304c3cbce4afe9e5247e21100037bff78"
|
||||
revision = "86e600f69ee4704c6efbf6a2a40a5c10700e76c2"
|
||||
|
||||
[[projects]]
|
||||
name = "google.golang.org/grpc"
|
||||
|
@ -459,6 +460,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "b6b2d696a242e715ddb8b25c93b3c8f7e7cabc9292eab29dffe935eddbd35fb8"
|
||||
inputs-digest = "fad966346d3b6042faf2bf793168b6ce9a8ff59ae207b7ad57008ead0f3ff7d4"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
|
||||
[[constraint]]
|
||||
name = "github.com/tendermint/go-amino"
|
||||
version = "~0.9.6"
|
||||
version = "~0.9.9"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/tendermint/iavl"
|
||||
|
@ -70,7 +70,7 @@
|
|||
|
||||
[[constraint]]
|
||||
name = "github.com/tendermint/tendermint"
|
||||
version = "0.19.0"
|
||||
version = "0.19.1"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/tendermint/tmlibs"
|
||||
|
|
34
Makefile
34
Makefile
|
@ -1,13 +1,14 @@
|
|||
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
|
||||
PACKAGES_NOCLITEST=$(shell go list ./... | grep -v '/vendor/' | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test)
|
||||
COMMIT_HASH := $(shell git rev-parse --short HEAD)
|
||||
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}"
|
||||
|
||||
all: check_tools get_vendor_deps build build_examples test
|
||||
all: check_tools get_vendor_deps install install_examples test_lint test
|
||||
|
||||
########################################
|
||||
### CI
|
||||
|
||||
ci: get_tools get_vendor_deps build test_cover
|
||||
ci: get_tools get_vendor_deps install test_cover test_lint test
|
||||
|
||||
########################################
|
||||
### Build
|
||||
|
@ -15,11 +16,11 @@ ci: get_tools get_vendor_deps build test_cover
|
|||
# This can be unified later, here for easy demos
|
||||
build:
|
||||
ifeq ($(OS),Windows_NT)
|
||||
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaiad
|
||||
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaiacli
|
||||
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad
|
||||
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli
|
||||
else
|
||||
go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaiad
|
||||
go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaiacli
|
||||
go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaia/cmd/gaiad
|
||||
go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaia/cmd/gaiacli
|
||||
endif
|
||||
|
||||
build_examples:
|
||||
|
@ -36,8 +37,8 @@ else
|
|||
endif
|
||||
|
||||
install:
|
||||
go install $(BUILD_FLAGS) ./cmd/gaiad
|
||||
go install $(BUILD_FLAGS) ./cmd/gaiacli
|
||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad
|
||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli
|
||||
|
||||
install_examples:
|
||||
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecoind
|
||||
|
@ -83,21 +84,22 @@ godocs:
|
|||
########################################
|
||||
### Testing
|
||||
|
||||
test: test_unit # test_cli
|
||||
test: test_unit test_cli
|
||||
|
||||
# Must be run in each package seperately for the visualization
|
||||
# Added here for easy reference
|
||||
# coverage:
|
||||
# go test -coverprofile=c.out && go tool cover -html=c.out
|
||||
test_cli:
|
||||
@go test -count 1 -p 1 `go list github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test`
|
||||
|
||||
test_unit:
|
||||
@go test $(PACKAGES)
|
||||
@go test $(PACKAGES_NOCLITEST)
|
||||
|
||||
test_cover:
|
||||
@bash tests/test_cover.sh
|
||||
|
||||
test_lint:
|
||||
gometalinter --disable-all --enable='golint' --vendor ./...
|
||||
|
||||
benchmark:
|
||||
@go test -bench=. $(PACKAGES)
|
||||
@go test -bench=. $(PACKAGES_NOCLITEST)
|
||||
|
||||
|
||||
########################################
|
||||
|
@ -127,4 +129,4 @@ devdoc_update:
|
|||
# To avoid unintended conflicts with file names, always add to .PHONY
|
||||
# unless there is a reason not to.
|
||||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
||||
.PHONY: build build_examples install install_examples dist check_tools get_tools get_vendor_deps draw_deps test test_unit test_tutorial benchmark devdoc_init devdoc devdoc_save devdoc_update
|
||||
.PHONY: build build_examples install install_examples dist check_tools get_tools get_vendor_deps draw_deps test test_cli test_unit test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
// Key to store the header in the DB itself.
|
||||
|
@ -29,6 +30,7 @@ type BaseApp struct {
|
|||
db dbm.DB // common DB backend
|
||||
cms sdk.CommitMultiStore // Main (uncached) state
|
||||
router Router // handle any kind of message
|
||||
codespacer *sdk.Codespacer // handle module codespacing
|
||||
|
||||
// must be set
|
||||
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
|
||||
|
@ -55,14 +57,20 @@ var _ abci.Application = (*BaseApp)(nil)
|
|||
|
||||
// Create and name new BaseApp
|
||||
// NOTE: The db is used to store the version number for now.
|
||||
func NewBaseApp(name string, logger log.Logger, db dbm.DB) *BaseApp {
|
||||
return &BaseApp{
|
||||
func NewBaseApp(name string, cdc *wire.Codec, logger log.Logger, db dbm.DB) *BaseApp {
|
||||
app := &BaseApp{
|
||||
Logger: logger,
|
||||
name: name,
|
||||
db: db,
|
||||
cms: store.NewCommitMultiStore(db),
|
||||
router: NewRouter(),
|
||||
codespacer: sdk.NewCodespacer(),
|
||||
txDecoder: defaultTxDecoder(cdc),
|
||||
}
|
||||
// Register the undefined & root codespaces, which should not be used by any modules
|
||||
app.codespacer.RegisterOrPanic(sdk.CodespaceUndefined)
|
||||
app.codespacer.RegisterOrPanic(sdk.CodespaceRoot)
|
||||
return app
|
||||
}
|
||||
|
||||
// BaseApp Name
|
||||
|
@ -70,6 +78,11 @@ func (app *BaseApp) Name() string {
|
|||
return app.name
|
||||
}
|
||||
|
||||
// Register the next available codespace through the baseapp's codespacer, starting from a default
|
||||
func (app *BaseApp) RegisterCodespace(codespace sdk.CodespaceType) sdk.CodespaceType {
|
||||
return app.codespacer.RegisterNext(codespace)
|
||||
}
|
||||
|
||||
// Mount a store to the provided key in the BaseApp multistore
|
||||
func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) {
|
||||
for _, key := range keys {
|
||||
|
@ -87,10 +100,31 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) {
|
|||
app.cms.MountStoreWithDB(key, typ, nil)
|
||||
}
|
||||
|
||||
// nolint - Set functions
|
||||
// Set the txDecoder function
|
||||
func (app *BaseApp) SetTxDecoder(txDecoder sdk.TxDecoder) {
|
||||
app.txDecoder = txDecoder
|
||||
}
|
||||
|
||||
// default custom logic for transaction decoding
|
||||
func defaultTxDecoder(cdc *wire.Codec) sdk.TxDecoder {
|
||||
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||
var tx = sdk.StdTx{}
|
||||
|
||||
if len(txBytes) == 0 {
|
||||
return nil, sdk.ErrTxDecode("txBytes are empty")
|
||||
}
|
||||
|
||||
// StdTx.Msg is an interface. The concrete types
|
||||
// are registered by MakeTxCodec
|
||||
err := cdc.UnmarshalBinary(txBytes, &tx)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrTxDecode("").Trace(err.Error())
|
||||
}
|
||||
return tx, nil
|
||||
}
|
||||
}
|
||||
|
||||
// nolint - Set functions
|
||||
func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) {
|
||||
app.initChainer = initChainer
|
||||
}
|
||||
|
@ -103,7 +137,6 @@ func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) {
|
|||
func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) {
|
||||
app.anteHandler = ah
|
||||
}
|
||||
|
||||
func (app *BaseApp) Router() Router { return app.router }
|
||||
|
||||
// load latest application version
|
||||
|
@ -177,9 +210,9 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error {
|
|||
// NewContext returns a new Context with the correct store, the given header, and nil txBytes.
|
||||
func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context {
|
||||
if isCheckTx {
|
||||
return sdk.NewContext(app.checkState.ms, header, true, nil)
|
||||
return sdk.NewContext(app.checkState.ms, header, true, nil, app.Logger)
|
||||
}
|
||||
return sdk.NewContext(app.deliverState.ms, header, false, nil)
|
||||
return sdk.NewContext(app.deliverState.ms, header, false, nil, app.Logger)
|
||||
}
|
||||
|
||||
type state struct {
|
||||
|
@ -195,7 +228,7 @@ func (app *BaseApp) setCheckState(header abci.Header) {
|
|||
ms := app.cms.CacheMultiStore()
|
||||
app.checkState = &state{
|
||||
ms: ms,
|
||||
ctx: sdk.NewContext(ms, header, true, nil),
|
||||
ctx: sdk.NewContext(ms, header, true, nil, app.Logger),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,11 +236,12 @@ func (app *BaseApp) setDeliverState(header abci.Header) {
|
|||
ms := app.cms.CacheMultiStore()
|
||||
app.deliverState = &state{
|
||||
ms: ms,
|
||||
ctx: sdk.NewContext(ms, header, false, nil),
|
||||
ctx: sdk.NewContext(ms, header, false, nil, app.Logger),
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
//______________________________________________________________________________
|
||||
|
||||
// ABCI
|
||||
|
||||
// Implements ABCI
|
||||
|
@ -231,7 +265,6 @@ func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOp
|
|||
// InitChain runs the initialization logic directly on the CommitMultiStore and commits it.
|
||||
func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) {
|
||||
if app.initChainer == nil {
|
||||
// TODO: should we have some default handling of validators?
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -241,7 +274,6 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
|
|||
|
||||
// NOTE: we don't commit, but BeginBlock for block 1
|
||||
// starts from this deliverState
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -311,9 +343,8 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
|
|||
if result.IsOK() {
|
||||
app.valUpdates = append(app.valUpdates, result.ValidatorUpdates...)
|
||||
} else {
|
||||
// Even though the Code is not OK, there will be some side
|
||||
// effects, like those caused by fee deductions or sequence
|
||||
// incrementations.
|
||||
// Even though the Result.Code is not OK, there are still effects,
|
||||
// namely fee deductions and sequence incrementing.
|
||||
}
|
||||
|
||||
// Tell the blockchain engine (i.e. Tendermint).
|
||||
|
@ -327,7 +358,7 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
|
|||
}
|
||||
}
|
||||
|
||||
// Mostly for testing
|
||||
// nolint- Mostly for testing
|
||||
func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) {
|
||||
return app.runTx(true, nil, tx)
|
||||
}
|
||||
|
@ -355,6 +386,7 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
|
|||
// Validate the Msg.
|
||||
err := msg.ValidateBasic()
|
||||
if err != nil {
|
||||
err = err.WithDefaultCodespace(sdk.CodespaceRoot)
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
|
@ -441,7 +473,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
|
|||
// Use the header from this latest block.
|
||||
app.setCheckState(header)
|
||||
|
||||
// Emtpy the Deliver state
|
||||
// Empty the Deliver state
|
||||
app.deliverState = nil
|
||||
|
||||
return abci.ResponseCommit{
|
||||
|
|
|
@ -25,7 +25,7 @@ func defaultLogger() log.Logger {
|
|||
func newBaseApp(name string) *BaseApp {
|
||||
logger := defaultLogger()
|
||||
db := dbm.NewMemDB()
|
||||
return NewBaseApp(name, logger, db)
|
||||
return NewBaseApp(name, nil, logger, db)
|
||||
}
|
||||
|
||||
func TestMountStores(t *testing.T) {
|
||||
|
@ -59,7 +59,7 @@ func TestLoadVersion(t *testing.T) {
|
|||
logger := defaultLogger()
|
||||
db := dbm.NewMemDB()
|
||||
name := t.Name()
|
||||
app := NewBaseApp(name, logger, db)
|
||||
app := NewBaseApp(name, nil, logger, db)
|
||||
|
||||
// make a cap key and mount the store
|
||||
capKey := sdk.NewKVStoreKey("main")
|
||||
|
@ -81,7 +81,7 @@ func TestLoadVersion(t *testing.T) {
|
|||
commitID := sdk.CommitID{1, res.Data}
|
||||
|
||||
// reload
|
||||
app = NewBaseApp(name, logger, db)
|
||||
app = NewBaseApp(name, nil, logger, db)
|
||||
app.MountStoresIAVL(capKey)
|
||||
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil
|
||||
assert.Nil(t, err)
|
||||
|
@ -147,7 +147,7 @@ func TestInitChainer(t *testing.T) {
|
|||
name := t.Name()
|
||||
db := dbm.NewMemDB()
|
||||
logger := defaultLogger()
|
||||
app := NewBaseApp(name, logger, db)
|
||||
app := NewBaseApp(name, nil, logger, db)
|
||||
// make cap keys and mount the stores
|
||||
// NOTE/TODO: mounting multiple stores is broken
|
||||
// see https://github.com/cosmos/cosmos-sdk/issues/532
|
||||
|
@ -184,7 +184,7 @@ func TestInitChainer(t *testing.T) {
|
|||
assert.Equal(t, value, res.Value)
|
||||
|
||||
// reload app
|
||||
app = NewBaseApp(name, logger, db)
|
||||
app = NewBaseApp(name, nil, logger, db)
|
||||
app.MountStoresIAVL(capKey, capKey2)
|
||||
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil
|
||||
assert.Nil(t, err)
|
||||
|
@ -240,7 +240,7 @@ func TestDeliverTx(t *testing.T) {
|
|||
height := int64((counter / txPerHeight) + 1)
|
||||
assert.Equal(t, height, thisHeader.Height)
|
||||
|
||||
counter += 1
|
||||
counter++
|
||||
return sdk.Result{}
|
||||
})
|
||||
|
||||
|
@ -319,7 +319,6 @@ type testUpdatePowerTx struct {
|
|||
const msgType = "testUpdatePowerTx"
|
||||
|
||||
func (tx testUpdatePowerTx) Type() string { return msgType }
|
||||
func (tx testUpdatePowerTx) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (tx testUpdatePowerTx) GetMsg() sdk.Msg { return tx }
|
||||
func (tx testUpdatePowerTx) GetSignBytes() []byte { return nil }
|
||||
func (tx testUpdatePowerTx) ValidateBasic() sdk.Error { return nil }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package core
|
||||
package context
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -126,7 +126,14 @@ func (ctx CoreContext) SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *w
|
|||
}
|
||||
|
||||
// sign and build the transaction from the msg
|
||||
func (ctx CoreContext) SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
func (ctx CoreContext) EnsureSignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (res *ctypes.ResultBroadcastTxCommit, err error) {
|
||||
|
||||
// default to next sequence number if none provided
|
||||
ctx, err = EnsureSequence(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
passphrase, err := ctx.GetPassphraseFromStdin(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -141,17 +148,22 @@ func (ctx CoreContext) SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Co
|
|||
}
|
||||
|
||||
// get the next sequence for the account address
|
||||
func (c CoreContext) NextSequence(address []byte) (int64, error) {
|
||||
if c.Decoder == nil {
|
||||
func (ctx CoreContext) NextSequence(address []byte) (int64, error) {
|
||||
if ctx.Decoder == nil {
|
||||
return 0, errors.New("AccountDecoder required but not provided")
|
||||
}
|
||||
|
||||
res, err := c.Query(address, c.AccountStore)
|
||||
res, err := ctx.Query(address, ctx.AccountStore)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
account, err := c.Decoder(res)
|
||||
if len(res) == 0 {
|
||||
fmt.Printf("No account found, defaulting to sequence 0\n")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
account, err := ctx.Decoder(res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package core
|
||||
package context
|
||||
|
||||
import (
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
|
@ -6,6 +6,7 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// typical context created in sdk modules for transactions/queries
|
||||
type CoreContext struct {
|
||||
ChainID string
|
||||
Height int64
|
|
@ -2,6 +2,7 @@ package context
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
|
@ -9,11 +10,10 @@ import (
|
|||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/core"
|
||||
)
|
||||
|
||||
// NewCoreContextFromViper - return a new context with parameters from the command line
|
||||
func NewCoreContextFromViper() core.CoreContext {
|
||||
func NewCoreContextFromViper() CoreContext {
|
||||
nodeURI := viper.GetString(client.FlagNode)
|
||||
var rpc rpcclient.Client
|
||||
if nodeURI != "" {
|
||||
|
@ -27,7 +27,7 @@ func NewCoreContextFromViper() core.CoreContext {
|
|||
chainID = def
|
||||
}
|
||||
}
|
||||
return core.CoreContext{
|
||||
return CoreContext{
|
||||
ChainID: chainID,
|
||||
Height: viper.GetInt64(client.FlagHeight),
|
||||
TrustNode: viper.GetBool(client.FlagTrustNode),
|
||||
|
@ -36,7 +36,7 @@ func NewCoreContextFromViper() core.CoreContext {
|
|||
Sequence: viper.GetInt64(client.FlagSequence),
|
||||
Client: rpc,
|
||||
Decoder: nil,
|
||||
AccountStore: "main",
|
||||
AccountStore: "acc",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,8 +54,9 @@ func defaultChainID() (string, error) {
|
|||
}
|
||||
|
||||
// EnsureSequence - automatically set sequence number if none provided
|
||||
func EnsureSequence(ctx core.CoreContext) (core.CoreContext, error) {
|
||||
if viper.IsSet(client.FlagSequence) {
|
||||
func EnsureSequence(ctx CoreContext) (CoreContext, error) {
|
||||
// Should be viper.IsSet, but this does not work - https://github.com/spf13/viper/pull/331
|
||||
if viper.GetInt64(client.FlagSequence) != 0 {
|
||||
return ctx, nil
|
||||
}
|
||||
from, err := ctx.GetFromAddress()
|
||||
|
|
|
@ -135,14 +135,17 @@ func printCreate(info keys.Info, seed string) {
|
|||
}
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// REST
|
||||
|
||||
// new key request REST body
|
||||
type NewKeyBody struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
Seed string `json:"seed"`
|
||||
}
|
||||
|
||||
// add new key REST handler
|
||||
func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var kb keys.Keybase
|
||||
var m NewKeyBody
|
||||
|
@ -208,6 +211,7 @@ func getSeed(algo keys.CryptoAlgo) string {
|
|||
return seed
|
||||
}
|
||||
|
||||
// Seed REST request handler
|
||||
func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
algoType := vars["type"]
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -18,14 +17,12 @@ func deleteKeyCommand() *cobra.Command {
|
|||
Use: "delete <name>",
|
||||
Short: "Delete the given key",
|
||||
RunE: runDeleteCmd,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDeleteCmd(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
return errors.New("You must provide a name for the key")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
buf := client.BufferStdin()
|
||||
|
@ -48,12 +45,15 @@ func runDeleteCmd(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
// REST
|
||||
|
||||
// delete key request REST body
|
||||
type DeleteKeyBody struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// delete key REST handler
|
||||
func DeleteKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
|
|
|
@ -31,8 +31,10 @@ func runListCmd(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
//REST
|
||||
/////////////////////////
|
||||
// REST
|
||||
|
||||
// query key list REST handler
|
||||
func QueryKeysRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
kb, err := GetKeyBase()
|
||||
if err != nil {
|
||||
|
|
|
@ -29,6 +29,7 @@ func Commands() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// resgister REST routes
|
||||
func RegisterRoutes(r *mux.Router) {
|
||||
r.HandleFunc("/keys", QueryKeysRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/keys", AddNewKeyRequestHandler).Methods("POST")
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -15,7 +14,15 @@ var showKeysCmd = &cobra.Command{
|
|||
Use: "show <name>",
|
||||
Short: "Show key info for the given name",
|
||||
Long: `Return public details of one local key.`,
|
||||
RunE: runShowCmd,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
info, err := getKey(name)
|
||||
if err == nil {
|
||||
printInfo(info)
|
||||
}
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
func getKey(name string) (keys.Info, error) {
|
||||
|
@ -27,23 +34,10 @@ func getKey(name string) (keys.Info, error) {
|
|||
return kb.Get(name)
|
||||
}
|
||||
|
||||
// CMD
|
||||
|
||||
func runShowCmd(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
return errors.New("You must provide a name for the key")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
info, err := getKey(name)
|
||||
if err == nil {
|
||||
printInfo(info)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
// REST
|
||||
|
||||
// get key REST handler
|
||||
func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -18,14 +17,12 @@ func updateKeyCommand() *cobra.Command {
|
|||
Use: "update <name>",
|
||||
Short: "Change the password used to protect private key",
|
||||
RunE: runUpdateCmd,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUpdateCmd(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
return errors.New("You must provide a name for the key")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
buf := client.BufferStdin()
|
||||
|
@ -53,13 +50,16 @@ func runUpdateCmd(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// REST
|
||||
|
||||
// update key request REST body
|
||||
type UpdateKeyBody struct {
|
||||
NewPassword string `json:"new_password"`
|
||||
OldPassword string `json:"old_password"`
|
||||
}
|
||||
|
||||
// update key REST handler
|
||||
func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package keys
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
|
@ -16,23 +19,18 @@ import (
|
|||
// KeyDBName is the directory under root where we store the keys
|
||||
const KeyDBName = "keys"
|
||||
|
||||
var (
|
||||
// keybase is used to make GetKeyBase a singleton
|
||||
keybase keys.Keybase
|
||||
)
|
||||
// keybase is used to make GetKeyBase a singleton
|
||||
var keybase keys.Keybase
|
||||
|
||||
// used for outputting keys.Info over REST
|
||||
type KeyOutput struct {
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
// TODO add pubkey?
|
||||
// Pubkey string `json:"pubkey"`
|
||||
// initialize a keybase based on the configuration
|
||||
func GetKeyBase() (keys.Keybase, error) {
|
||||
rootDir := viper.GetString(cli.HomeFlag)
|
||||
return GetKeyBaseFromDir(rootDir)
|
||||
}
|
||||
|
||||
// GetKeyBase initializes a keybase based on the configuration
|
||||
func GetKeyBase() (keys.Keybase, error) {
|
||||
// initialize a keybase based on the configuration
|
||||
func GetKeyBaseFromDir(rootDir string) (keys.Keybase, error) {
|
||||
if keybase == nil {
|
||||
rootDir := viper.GetString(cli.HomeFlag)
|
||||
db, err := dbm.NewGoLevelDB(KeyDBName, filepath.Join(rootDir, "keys"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -47,36 +45,57 @@ func SetKeyBase(kb keys.Keybase) {
|
|||
keybase = kb
|
||||
}
|
||||
|
||||
// used for outputting keys.Info over REST
|
||||
type KeyOutput struct {
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
PubKey string `json:"pub_key"`
|
||||
}
|
||||
|
||||
func NewKeyOutput(info keys.Info) KeyOutput {
|
||||
return KeyOutput{
|
||||
Name: info.Name,
|
||||
Address: info.PubKey.Address().String(),
|
||||
PubKey: strings.ToUpper(hex.EncodeToString(info.PubKey.Bytes())),
|
||||
}
|
||||
}
|
||||
|
||||
func NewKeyOutputs(infos []keys.Info) []KeyOutput {
|
||||
kos := make([]KeyOutput, len(infos))
|
||||
for i, info := range infos {
|
||||
kos[i] = NewKeyOutput(info)
|
||||
}
|
||||
return kos
|
||||
}
|
||||
|
||||
func printInfo(info keys.Info) {
|
||||
ko := NewKeyOutput(info)
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
addr := info.PubKey.Address().String()
|
||||
sep := "\t\t"
|
||||
if len(info.Name) > 7 {
|
||||
sep = "\t"
|
||||
}
|
||||
fmt.Printf("%s%s%s\n", info.Name, sep, addr)
|
||||
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\tPUBKEY:\n")
|
||||
fmt.Printf("%s\t%s\t%s\n", ko.Name, ko.Address, ko.PubKey)
|
||||
case "json":
|
||||
json, err := MarshalJSON(info)
|
||||
out, err := json.MarshalIndent(ko, "", "\t")
|
||||
if err != nil {
|
||||
panic(err) // really shouldn't happen...
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(json))
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func printInfos(infos []keys.Info) {
|
||||
kos := NewKeyOutputs(infos)
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
fmt.Println("All keys:")
|
||||
for _, i := range infos {
|
||||
printInfo(i)
|
||||
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\tPUBKEY:\n")
|
||||
for _, ko := range kos {
|
||||
fmt.Printf("%s\t%s\t%s\n", ko.Name, ko.Address, ko.PubKey)
|
||||
}
|
||||
case "json":
|
||||
json, err := MarshalJSON(infos)
|
||||
out, err := json.MarshalIndent(kos, "", "\t")
|
||||
if err != nil {
|
||||
panic(err) // really shouldn't happen...
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(json))
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,12 @@ func init() {
|
|||
wire.RegisterCrypto(cdc)
|
||||
}
|
||||
|
||||
// marshal keys
|
||||
func MarshalJSON(o interface{}) ([]byte, error) {
|
||||
return cdc.MarshalJSON(o)
|
||||
}
|
||||
|
||||
// unmarshal json
|
||||
func UnmarshalJSON(bz []byte, ptr interface{}) error {
|
||||
return cdc.UnmarshalJSON(bz, ptr)
|
||||
}
|
||||
|
|
|
@ -7,34 +7,14 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
|
||||
)
|
||||
|
||||
var globalConfig *cfg.Config
|
||||
|
||||
func waitForRPC() {
|
||||
laddr := GetConfig().RPC.ListenAddress
|
||||
fmt.Println("LADDR", laddr)
|
||||
client := rpcclient.NewJSONRPCClient(laddr)
|
||||
ctypes.RegisterAmino(client.Codec())
|
||||
result := new(ctypes.ResultStatus)
|
||||
for {
|
||||
_, err := client.Call("status", map[string]interface{}{}, result)
|
||||
if err == nil {
|
||||
return
|
||||
} else {
|
||||
fmt.Println("error", err)
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// f**ing long, but unique for each test
|
||||
func makePathname() string {
|
||||
// get path
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -34,6 +33,7 @@ import (
|
|||
keys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
bapp "github.com/cosmos/cosmos-sdk/examples/basecoin/app"
|
||||
btypes "github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
||||
tests "github.com/cosmos/cosmos-sdk/tests"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
|
@ -42,9 +42,9 @@ var (
|
|||
coinAmount = int64(10000000)
|
||||
|
||||
// XXX bad globals
|
||||
name = "test"
|
||||
password = "0123456789"
|
||||
port string // XXX: but it's the int ...
|
||||
name string = "test"
|
||||
password string = "0123456789"
|
||||
seed string
|
||||
sendAddr string
|
||||
)
|
||||
|
@ -80,7 +80,7 @@ func TestKeys(t *testing.T) {
|
|||
jsonStr = []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed": "%s"}`, newName, newPassword, newSeed))
|
||||
res, body = request(t, port, "POST", "/keys", jsonStr)
|
||||
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
addr := body
|
||||
assert.Len(t, addr, 40, "Returned address has wrong format", addr)
|
||||
|
||||
|
@ -157,7 +157,7 @@ func TestNodeStatus(t *testing.T) {
|
|||
|
||||
func TestBlock(t *testing.T) {
|
||||
|
||||
waitForHeight(2)
|
||||
tests.WaitForHeight(2, port)
|
||||
|
||||
var resultBlock ctypes.ResultBlock
|
||||
|
||||
|
@ -224,7 +224,7 @@ func TestCoinSend(t *testing.T) {
|
|||
|
||||
// create TX
|
||||
receiveAddr, resultTx := doSend(t, port, seed)
|
||||
waitForHeight(resultTx.Height + 1)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// check if tx was commited
|
||||
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||
|
@ -253,7 +253,7 @@ func TestIBCTransfer(t *testing.T) {
|
|||
// create TX
|
||||
resultTx := doIBCTransfer(t, port, seed)
|
||||
|
||||
waitForHeight(resultTx.Height + 1)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// check if tx was commited
|
||||
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||
|
@ -286,7 +286,7 @@ func TestTxs(t *testing.T) {
|
|||
// create TX
|
||||
_, resultTx := doSend(t, port, seed)
|
||||
|
||||
waitForHeight(resultTx.Height + 1)
|
||||
tests.WaitForHeight(resultTx.Height+1, port)
|
||||
|
||||
// check if tx is findable
|
||||
res, body := request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
|
||||
|
@ -311,7 +311,11 @@ func TestTxs(t *testing.T) {
|
|||
// strt TM and the LCD in process, listening on their respective sockets
|
||||
func startTMAndLCD() (*nm.Node, net.Listener, error) {
|
||||
|
||||
viper.Set(cli.HomeFlag, os.TempDir())
|
||||
dir, err := ioutil.TempDir("", "lcd_test")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
viper.Set(cli.HomeFlag, dir)
|
||||
kb, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -376,7 +380,7 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
waitForStart()
|
||||
tests.WaitForStart(port)
|
||||
|
||||
return node, lcd, nil
|
||||
}
|
||||
|
@ -403,7 +407,7 @@ func startTM(cfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, p
|
|||
}
|
||||
|
||||
// wait for rpc
|
||||
waitForRPC()
|
||||
tests.WaitForRPC(GetConfig().RPC.ListenAddress)
|
||||
|
||||
logger.Info("Tendermint running!")
|
||||
return n, err
|
||||
|
@ -486,71 +490,3 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
|
|||
|
||||
return resultTx
|
||||
}
|
||||
|
||||
func waitForHeight(height int64) {
|
||||
for {
|
||||
var resultBlock ctypes.ResultBlock
|
||||
|
||||
url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest")
|
||||
res, err := http.Get(url)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
err = cdc.UnmarshalJSON([]byte(body), &resultBlock)
|
||||
if err != nil {
|
||||
fmt.Println("RES", res)
|
||||
fmt.Println("BODY", string(body))
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if resultBlock.Block.Height >= height {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
}
|
||||
|
||||
// wait for 2 blocks
|
||||
func waitForStart() {
|
||||
waitHeight := int64(2)
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest")
|
||||
res, err := http.Get(url)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// waiting for server to start ...
|
||||
if res.StatusCode != http.StatusOK {
|
||||
res.Body.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
res.Body.Close()
|
||||
|
||||
resultBlock := new(ctypes.ResultBlock)
|
||||
err = cdc.UnmarshalJSON([]byte(body), &resultBlock)
|
||||
if err != nil {
|
||||
fmt.Println("RES", res)
|
||||
fmt.Println("BODY", string(body))
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if resultBlock.Block.Height >= waitHeight {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,14 +13,15 @@ import (
|
|||
cmn "github.com/tendermint/tmlibs/common"
|
||||
|
||||
client "github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
keys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
rpc "github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
tx "github.com/cosmos/cosmos-sdk/client/tx"
|
||||
version "github.com/cosmos/cosmos-sdk/version"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth/rest"
|
||||
bank "github.com/cosmos/cosmos-sdk/x/bank/rest"
|
||||
ibc "github.com/cosmos/cosmos-sdk/x/ibc/rest"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
||||
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
|
||||
ibc "github.com/cosmos/cosmos-sdk/x/ibc/client/rest"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -66,19 +67,21 @@ func startRESTServerFn(cdc *wire.Codec) func(cmd *cobra.Command, args []string)
|
|||
|
||||
func createHandler(cdc *wire.Codec) http.Handler {
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/version", version.VersionRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/version", version.RequestHandler).Methods("GET")
|
||||
|
||||
kb, err := keys.GetKeyBase() //XXX
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
|
||||
// TODO make more functional? aka r = keys.RegisterRoutes(r)
|
||||
keys.RegisterRoutes(r)
|
||||
rpc.RegisterRoutes(r)
|
||||
tx.RegisterRoutes(r, cdc)
|
||||
auth.RegisterRoutes(r, cdc, "main")
|
||||
bank.RegisterRoutes(r, cdc, kb)
|
||||
ibc.RegisterRoutes(r, cdc, kb)
|
||||
rpc.RegisterRoutes(ctx, r)
|
||||
tx.RegisterRoutes(ctx, r, cdc)
|
||||
auth.RegisterRoutes(ctx, r, cdc, "acc")
|
||||
bank.RegisterRoutes(ctx, r, cdc, kb)
|
||||
ibc.RegisterRoutes(ctx, r, cdc, kb)
|
||||
return r
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ func blockCommand() *cobra.Command {
|
|||
cmd := &cobra.Command{
|
||||
Use: "block [height]",
|
||||
Short: "Get verified data for a the block at given height",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: printBlock,
|
||||
}
|
||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
|
||||
|
@ -29,9 +30,8 @@ func blockCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func getBlock(height *int64) ([]byte, error) {
|
||||
func getBlock(ctx context.CoreContext, height *int64) ([]byte, error) {
|
||||
// get the node
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -55,8 +55,9 @@ func getBlock(height *int64) ([]byte, error) {
|
|||
return output, nil
|
||||
}
|
||||
|
||||
func GetChainHeight() (int64, error) {
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
// get the current blockchain height
|
||||
func GetChainHeight(ctx context.CoreContext) (int64, error) {
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
@ -84,7 +85,7 @@ func printBlock(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
}
|
||||
|
||||
output, err := getBlock(height)
|
||||
output, err := getBlock(context.NewCoreContextFromViper(), height)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -94,7 +95,9 @@ func printBlock(cmd *cobra.Command, args []string) error {
|
|||
|
||||
// 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)
|
||||
height, err := strconv.ParseInt(vars["height"], 10, 64)
|
||||
if err != nil {
|
||||
|
@ -102,33 +105,37 @@ func BlockRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/block/{height}'."))
|
||||
return
|
||||
}
|
||||
chainHeight, err := GetChainHeight()
|
||||
chainHeight, err := GetChainHeight(ctx)
|
||||
if height > chainHeight {
|
||||
w.WriteHeader(404)
|
||||
w.Write([]byte("ERROR: Requested block height is bigger then the chain length."))
|
||||
return
|
||||
}
|
||||
output, err := getBlock(&height)
|
||||
output, err := getBlock(ctx, &height)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
|
||||
func LatestBlockRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
height, err := GetChainHeight()
|
||||
// REST handler to get the latest block
|
||||
func LatestBlockRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
height, err := GetChainHeight(ctx)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
output, err := getBlock(&height)
|
||||
output, err := getBlock(ctx, &height)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -44,11 +45,12 @@ func initClientCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func RegisterRoutes(r *mux.Router) {
|
||||
r.HandleFunc("/node_info", NodeInfoRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/syncing", NodeSyncingRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/blocks/latest", LatestBlockRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/blocks/{height}", BlockRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/validatorsets/latest", LatestValidatorsetRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/validatorsets/{height}", ValidatorsetRequestHandler).Methods("GET")
|
||||
// Register REST endpoints
|
||||
func RegisterRoutes(ctx context.CoreContext, r *mux.Router) {
|
||||
r.HandleFunc("/node_info", NodeInfoRequestHandlerFn(ctx)).Methods("GET")
|
||||
r.HandleFunc("/syncing", NodeSyncingRequestHandlerFn(ctx)).Methods("GET")
|
||||
r.HandleFunc("/blocks/latest", LatestBlockRequestHandlerFn(ctx)).Methods("GET")
|
||||
r.HandleFunc("/blocks/{height}", BlockRequestHandlerFn(ctx)).Methods("GET")
|
||||
r.HandleFunc("/validatorsets/latest", LatestValidatorSetRequestHandlerFn(ctx)).Methods("GET")
|
||||
r.HandleFunc("/validatorsets/{height}", ValidatorSetRequestHandlerFn(ctx)).Methods("GET")
|
||||
}
|
||||
|
|
|
@ -22,9 +22,9 @@ func statusCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func getNodeStatus() (*ctypes.ResultStatus, error) {
|
||||
func getNodeStatus(ctx context.CoreContext) (*ctypes.ResultStatus, error) {
|
||||
// get the node
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return &ctypes.ResultStatus{}, err
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func getNodeStatus() (*ctypes.ResultStatus, error) {
|
|||
// CMD
|
||||
|
||||
func printNodeStatus(cmd *cobra.Command, args []string) error {
|
||||
status, err := getNodeStatus()
|
||||
status, err := getNodeStatus(context.NewCoreContextFromViper())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -51,8 +51,10 @@ func printNodeStatus(cmd *cobra.Command, args []string) error {
|
|||
|
||||
// REST
|
||||
|
||||
func NodeInfoRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
status, err := getNodeStatus()
|
||||
// REST handler for node info
|
||||
func NodeInfoRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
status, err := getNodeStatus(ctx)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -67,10 +69,13 @@ func NodeInfoRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
|
||||
func NodeSyncingRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
status, err := getNodeStatus()
|
||||
// REST handler for node syncing
|
||||
func NodeSyncingRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
status, err := getNodeStatus(ctx)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -84,4 +89,5 @@ func NodeSyncingRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
w.Write([]byte(strconv.FormatBool(syncing)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,13 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
)
|
||||
|
||||
// TODO these next two functions feel kinda hacky based on their placement
|
||||
|
||||
func validatorCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "validatorset <height>",
|
||||
Use: "validatorset [height]",
|
||||
Short: "Get the full validator set at given height",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: printValidators,
|
||||
}
|
||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
|
||||
|
@ -24,9 +27,9 @@ func validatorCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func GetValidators(height *int64) ([]byte, error) {
|
||||
func getValidators(ctx context.CoreContext, height *int64) ([]byte, error) {
|
||||
// get the node
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -59,7 +62,7 @@ func printValidators(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
}
|
||||
|
||||
output, err := GetValidators(height)
|
||||
output, err := getValidators(context.NewCoreContextFromViper(), height)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -70,7 +73,9 @@ func printValidators(cmd *cobra.Command, args []string) error {
|
|||
|
||||
// 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)
|
||||
height, err := strconv.ParseInt(vars["height"], 10, 64)
|
||||
if err != nil {
|
||||
|
@ -78,33 +83,37 @@ func ValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/validatorsets/{height}'."))
|
||||
return
|
||||
}
|
||||
chainHeight, err := GetChainHeight()
|
||||
chainHeight, err := GetChainHeight(ctx)
|
||||
if height > chainHeight {
|
||||
w.WriteHeader(404)
|
||||
w.Write([]byte("ERROR: Requested block height is bigger then the chain length."))
|
||||
return
|
||||
}
|
||||
output, err := GetValidators(&height)
|
||||
output, err := getValidators(ctx, &height)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
|
||||
func LatestValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
height, err := GetChainHeight()
|
||||
// Latest Validator Set REST handler
|
||||
func LatestValidatorSetRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
height, err := GetChainHeight(ctx)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
output, err := GetValidators(&height)
|
||||
output, err := getValidators(ctx, &height)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,11 +7,14 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
)
|
||||
|
||||
// Tx Broadcast Body
|
||||
type BroadcastTxBody struct {
|
||||
TxBytes string `json="tx"`
|
||||
}
|
||||
|
||||
func BroadcastTxRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// BroadcastTx REST Handler
|
||||
func BroadcastTxRequestHandlerFn(ctx context.CoreContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var m BroadcastTxBody
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
|
@ -22,7 +25,7 @@ func BroadcastTxRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
res, err := context.NewCoreContextFromViper().BroadcastTx([]byte(m.TxBytes))
|
||||
res, err := ctx.BroadcastTx([]byte(m.TxBytes))
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -30,4 +33,5 @@ func BroadcastTxRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
w.Write([]byte(string(res.Height)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"strconv"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
|
@ -21,26 +20,42 @@ import (
|
|||
)
|
||||
|
||||
// Get the default command for a tx query
|
||||
func QueryTxCmd(cmdr commander) *cobra.Command {
|
||||
func QueryTxCmd(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "tx [hash]",
|
||||
Short: "Matches this txhash over all committed blocks",
|
||||
RunE: cmdr.queryAndPrintTx,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// find the key to look up the account
|
||||
hashHexStr := args[0]
|
||||
trustNode := viper.GetBool(client.FlagTrustNode)
|
||||
|
||||
output, err := queryTx(cdc, context.NewCoreContextFromViper(), hashHexStr, trustNode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
|
||||
|
||||
// TODO: change this to false when we can
|
||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c commander) queryTx(hashHexStr string, trustNode bool) ([]byte, error) {
|
||||
func queryTx(cdc *wire.Codec, ctx context.CoreContext, hashHexStr string, trustNode bool) ([]byte, error) {
|
||||
hash, err := hex.DecodeString(hashHexStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get the node
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -49,7 +64,7 @@ func (c commander) queryTx(hashHexStr string, trustNode bool) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info, err := formatTxResult(c.cdc, res)
|
||||
info, err := formatTxResult(cdc, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -88,31 +103,10 @@ func parseTx(cdc *wire.Codec, txBytes []byte) (sdk.Tx, error) {
|
|||
return tx, nil
|
||||
}
|
||||
|
||||
// CMD
|
||||
|
||||
// command to query for a transaction
|
||||
func (c commander) queryAndPrintTx(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
return errors.New("You must provide a tx hash")
|
||||
}
|
||||
|
||||
// find the key to look up the account
|
||||
hashHexStr := args[0]
|
||||
trustNode := viper.GetBool(client.FlagTrustNode)
|
||||
|
||||
output, err := c.queryTx(hashHexStr, trustNode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// REST
|
||||
|
||||
func QueryTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Request) {
|
||||
c := commander{cdc}
|
||||
// transaction query REST handler
|
||||
func QueryTxRequestHandlerFn(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
hashHexStr := vars["hash"]
|
||||
|
@ -122,7 +116,7 @@ func QueryTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Requ
|
|||
trustNode = true
|
||||
}
|
||||
|
||||
output, err := c.queryTx(hashHexStr, trustNode)
|
||||
output, err := queryTx(cdc, ctx, hashHexStr, trustNode)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -4,26 +4,22 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
// type used to pass around the provided cdc
|
||||
type commander struct {
|
||||
cdc *wire.Codec
|
||||
}
|
||||
|
||||
// AddCommands adds a number of tx-query related subcommands
|
||||
func AddCommands(cmd *cobra.Command, cdc *wire.Codec) {
|
||||
cmdr := commander{cdc}
|
||||
cmd.AddCommand(
|
||||
SearchTxCmd(cmdr),
|
||||
QueryTxCmd(cmdr),
|
||||
SearchTxCmd(cdc),
|
||||
QueryTxCmd(cdc),
|
||||
)
|
||||
}
|
||||
|
||||
func RegisterRoutes(r *mux.Router, cdc *wire.Codec) {
|
||||
// register REST routes
|
||||
func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec) {
|
||||
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, ctx)).Methods("GET")
|
||||
// r.HandleFunc("/txs", SearchTxRequestHandler(cdc)).Methods("GET")
|
||||
r.HandleFunc("/txs/{hash}", QueryTxRequestHandler(cdc)).Methods("GET")
|
||||
// r.HandleFunc("/txs/sign", SignTxRequstHandler).Methods("POST")
|
||||
// r.HandleFunc("/txs/broadcast", BroadcastTxRequestHandler).Methods("POST")
|
||||
}
|
||||
|
|
|
@ -22,13 +22,24 @@ const (
|
|||
)
|
||||
|
||||
// default client command to search through tagged transactions
|
||||
func SearchTxCmd(cmdr commander) *cobra.Command {
|
||||
func SearchTxCmd(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "txs",
|
||||
Short: "Search for all transactions that match the given tags",
|
||||
RunE: cmdr.searchAndPrintTx,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
tags := viper.GetStringSlice(flagTags)
|
||||
|
||||
output, err := searchTx(context.NewCoreContextFromViper(), cdc, tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
|
||||
|
||||
// TODO: change this to false once proofs built in
|
||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
||||
cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)")
|
||||
|
@ -36,7 +47,7 @@ func SearchTxCmd(cmdr commander) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (c commander) searchTx(tags []string) ([]byte, error) {
|
||||
func searchTx(ctx context.CoreContext, cdc *wire.Codec, tags []string) ([]byte, error) {
|
||||
if len(tags) == 0 {
|
||||
return nil, errors.New("Must declare at least one tag to search")
|
||||
}
|
||||
|
@ -44,7 +55,7 @@ func (c commander) searchTx(tags []string) ([]byte, error) {
|
|||
query := strings.Join(tags, " AND ")
|
||||
|
||||
// get the node
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -55,12 +66,12 @@ func (c commander) searchTx(tags []string) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info, err := formatTxResults(c.cdc, res)
|
||||
info, err := formatTxResults(cdc, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output, err := c.cdc.MarshalJSON(info)
|
||||
output, err := cdc.MarshalJSON(info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -79,24 +90,11 @@ func formatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]txInfo, error)
|
|||
return out, nil
|
||||
}
|
||||
|
||||
// CMD
|
||||
|
||||
func (c commander) searchAndPrintTx(cmd *cobra.Command, args []string) error {
|
||||
tags := viper.GetStringSlice(flagTags)
|
||||
|
||||
output, err := c.searchTx(tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// REST
|
||||
|
||||
func SearchTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Request) {
|
||||
c := commander{cdc}
|
||||
// Search Tx REST Handler
|
||||
func SearchTxRequestHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
tag := r.FormValue("tag")
|
||||
if tag == "" {
|
||||
|
@ -106,7 +104,7 @@ func SearchTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Req
|
|||
}
|
||||
|
||||
tags := []string{tag}
|
||||
output, err := c.searchTx(tags)
|
||||
output, err := searchTx(ctx, cdc, tags)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -8,12 +8,15 @@ import (
|
|||
keys "github.com/tendermint/go-crypto/keys"
|
||||
)
|
||||
|
||||
// REST request body
|
||||
// TODO does this need to be exposed?
|
||||
type SignTxBody struct {
|
||||
Name string `json="name"`
|
||||
Password string `json="password"`
|
||||
TxBytes string `json="tx"`
|
||||
}
|
||||
|
||||
// sign transaction REST Handler
|
||||
func SignTxRequstHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var kb keys.Keybase
|
||||
var m SignTxBody
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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{})
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
|
@ -12,19 +10,15 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client/lcd"
|
||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
|
||||
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands"
|
||||
simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
|
||||
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli"
|
||||
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
)
|
||||
|
||||
// TODO: distinguish from basecli
|
||||
|
||||
// rootCmd is the entry point for this binary
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
|
@ -34,10 +28,7 @@ var (
|
|||
)
|
||||
|
||||
func main() {
|
||||
// disable sorting
|
||||
cobra.EnableCommandSorting = false
|
||||
|
||||
// get the codec
|
||||
cdc := app.MakeCodec()
|
||||
|
||||
// TODO: setup keybase, viper object, etc. to be passed into
|
||||
|
@ -53,24 +44,21 @@ func main() {
|
|||
// add query/post commands (custom to binary)
|
||||
rootCmd.AddCommand(
|
||||
client.GetCommands(
|
||||
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
|
||||
authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)),
|
||||
stakecmd.GetCmdQueryCandidate("stake", cdc),
|
||||
//stakecmd.GetCmdQueryCandidates("stake", cdc),
|
||||
stakecmd.GetCmdQueryDelegatorBond("stake", cdc),
|
||||
//stakecmd.GetCmdQueryDelegatorBonds("stake", cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
bankcmd.SendTxCmd(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCTransferCmd(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCRelayCmd(cdc),
|
||||
simplestakingcmd.BondTxCmd(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
simplestakingcmd.UnbondTxCmd(cdc),
|
||||
stakecmd.GetCmdDeclareCandidacy(cdc),
|
||||
stakecmd.GetCmdEditCandidacy(cdc),
|
||||
stakecmd.GetCmdDelegate(cdc),
|
||||
stakecmd.GetCmdUnbond(cdc),
|
||||
)...)
|
||||
|
||||
// add proxy, version and key info
|
||||
|
@ -83,6 +71,6 @@ func main() {
|
|||
)
|
||||
|
||||
// prepare and add flags
|
||||
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.gaiacli"))
|
||||
executor := cli.PrepareMainCmd(rootCmd, "GA", app.DefaultCLIHome)
|
||||
executor.Execute()
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -47,7 +47,7 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'Cosmos-SDK'
|
||||
copyright = u'2017, The Authors'
|
||||
copyright = u'2018, The Authors'
|
||||
author = u'The Authors'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
@ -69,7 +69,7 @@ language = None
|
|||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'old']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
|
|
@ -29,9 +29,6 @@ type Msg interface {
|
|||
// Must be alphanumeric or empty.
|
||||
Type() string
|
||||
|
||||
// Get some property of the Msg.
|
||||
Get(key interface{}) (value interface{})
|
||||
|
||||
// Get the canonical byte representation of the Msg.
|
||||
GetSignBytes() []byte
|
||||
|
||||
|
@ -63,18 +60,15 @@ Messages can specify basic self-consistency checks using the `ValidateBasic()`
|
|||
method to enforce that message contents are well formed before any actual logic
|
||||
begins.
|
||||
|
||||
Finally, messages can provide generic access to their contents via `Get(key)`,
|
||||
but this is mostly for convenience and not type-safe.
|
||||
|
||||
For instance, the `Basecoin` message types are defined in `x/bank/tx.go`:
|
||||
|
||||
```go
|
||||
type SendMsg struct {
|
||||
type MsgSend struct {
|
||||
Inputs []Input `json:"inputs"`
|
||||
Outputs []Output `json:"outputs"`
|
||||
}
|
||||
|
||||
type IssueMsg struct {
|
||||
type MsgIssue struct {
|
||||
Banker sdk.Address `json:"banker"`
|
||||
Outputs []Output `json:"outputs"`
|
||||
}
|
||||
|
@ -83,7 +77,7 @@ type IssueMsg struct {
|
|||
Each specifies the addresses that must sign the message:
|
||||
|
||||
```go
|
||||
func (msg SendMsg) GetSigners() []sdk.Address {
|
||||
func (msg MsgSend) GetSigners() []sdk.Address {
|
||||
addrs := make([]sdk.Address, len(msg.Inputs))
|
||||
for i, in := range msg.Inputs {
|
||||
addrs[i] = in.Address
|
||||
|
@ -91,7 +85,7 @@ func (msg SendMsg) GetSigners() []sdk.Address {
|
|||
return addrs
|
||||
}
|
||||
|
||||
func (msg IssueMsg) GetSigners() []sdk.Address {
|
||||
func (msg MsgIssue) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.Banker}
|
||||
}
|
||||
```
|
||||
|
@ -174,13 +168,13 @@ property that it can unmarshal into interface types, but it requires the
|
|||
relevant types to be registered ahead of type. Registration happens on a
|
||||
`Codec` object, so as not to taint the global name space.
|
||||
|
||||
For instance, in `Basecoin`, we wish to register the `SendMsg` and `IssueMsg`
|
||||
For instance, in `Basecoin`, we wish to register the `MsgSend` and `MsgIssue`
|
||||
types:
|
||||
|
||||
```go
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(bank.SendMsg{}, "cosmos-sdk/SendMsg", nil)
|
||||
cdc.RegisterConcrete(bank.IssueMsg{}, "cosmos-sdk/IssueMsg", nil)
|
||||
cdc.RegisterConcrete(bank.MsgSend{}, "cosmos-sdk/MsgSend", nil)
|
||||
cdc.RegisterConcrete(bank.MsgIssue{}, "cosmos-sdk/MsgIssue", nil)
|
||||
```
|
||||
|
||||
Note how each concrete type is given a name - these name determine the type's
|
||||
|
@ -319,8 +313,8 @@ func NewHandler(am sdk.AccountMapper) sdk.Handler {
|
|||
The quintessential SDK application is Basecoin - a simple
|
||||
multi-asset cryptocurrency. Basecoin consists of a set of
|
||||
accounts stored in a Merkle tree, where each account may have
|
||||
many coins. There are two message types: SendMsg and IssueMsg.
|
||||
SendMsg allows coins to be sent around, while IssueMsg allows a
|
||||
many coins. There are two message types: MsgSend and MsgIssue.
|
||||
MsgSend allows coins to be sent around, while MsgIssue allows a
|
||||
set of predefined users to issue new coins.
|
||||
|
||||
## Conclusion
|
||||
|
|
|
@ -14,14 +14,13 @@ Welcome to the Cosmos SDK!
|
|||
SDK
|
||||
---
|
||||
|
||||
.. One maxdepth for now
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
sdk/overview.rst
|
||||
sdk/install.rst
|
||||
sdk/glossary.rst
|
||||
sdk/key-management.rst
|
||||
.. sdk/overview.rst # needs to be updated
|
||||
.. old/glossary.rst # not completely up to date but has good content
|
||||
|
||||
.. Basecoin
|
||||
.. --------
|
||||
|
@ -29,19 +28,17 @@ SDK
|
|||
.. .. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
.. basecoin/basics.rst
|
||||
.. basecoin/extensions.rst
|
||||
.. old/basecoin/basics.rst # has a decent getting-start tutorial that's relatively up to date, should be consolidated with the other getting started doc
|
||||
|
||||
Extensions
|
||||
----------
|
||||
.. Extensions
|
||||
.. ----------
|
||||
|
||||
Replay Protection
|
||||
~~~~~~~~~~~~~~~~~
|
||||
.. old/basecoin/extensions.rst # probably not worth salvaging
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
.. Replay Protection
|
||||
.. ~~~~~~~~~~~~~~~~~
|
||||
|
||||
x/replay-protection.rst
|
||||
.. old/replay-protection.rst # not sure if worth salvaging
|
||||
|
||||
|
||||
Staking
|
||||
|
@ -50,17 +47,13 @@ Staking
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
staking/intro.rst
|
||||
staking/key-management.rst
|
||||
staking/local-testnet.rst
|
||||
staking/public-testnet.rst
|
||||
staking/testnet.rst
|
||||
.. staking/intro.rst
|
||||
.. staking/key-management.rst
|
||||
.. staking/local-testnet.rst
|
||||
.. staking/public-testnet.rst
|
||||
|
||||
Extras
|
||||
------
|
||||
.. IBC
|
||||
.. ---
|
||||
|
||||
.. One maxdepth for now
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ibc.rst
|
||||
.. old/ibc.rst # needs to be updated
|
||||
|
|
|
@ -12,7 +12,7 @@ Get Tokens
|
|||
If you haven't already `created a key <../key-management.html>`__,
|
||||
do so now. Copy your key's address and enter it into
|
||||
`this utility <http://www.cosmosvalidators.com/>`__ which will send you
|
||||
some ``fermion`` testnet tokens.
|
||||
some ``steak`` testnet tokens.
|
||||
|
||||
Get Files
|
||||
---------
|
||||
|
@ -59,6 +59,6 @@ and check our balance:
|
|||
|
||||
Where ``$MYADDR`` is the address originally generated by ``gaia keys new bob``.
|
||||
|
||||
You are now ready to declare candidacy or delegate some fermions. See the
|
||||
You are now ready to declare candidacy or delegate some steaks. See the
|
||||
`staking module overview <./staking-module.html>`__ for more information
|
||||
on using the ``gaia client``.
|
|
@ -15,7 +15,7 @@ while defining as little about that state machine as possible (staying true to t
|
|||
BaseApp requires stores to be mounted via capabilities keys - handlers can only access
|
||||
stores they're given the key for. The BaseApp ensures all stores are properly loaded, cached, and committed.
|
||||
One mounted store is considered the "main" - it holds the latest block header, from which we can find and load the
|
||||
most recent state.
|
||||
most recent state ([TODO](https://github.com/cosmos/cosmos-sdk/issues/522)).
|
||||
|
||||
BaseApp distinguishes between two handler types - the `AnteHandler` and the `MsgHandler`.
|
||||
The former is a global validity check (checking nonces, sigs and sufficient balances to pay fees,
|
|
@ -1,16 +1,12 @@
|
|||
Install
|
||||
=======
|
||||
|
||||
If you aren't used to compile go programs and just want the released
|
||||
version of the code, please head to our
|
||||
`downloads <https://tendermint.com/download>`__ page to get a
|
||||
pre-compiled binary for your platform.
|
||||
|
||||
Usually, Cosmos SDK can be installed like a normal Go program:
|
||||
Cosmos SDK can be installed to
|
||||
``$GOPATH/src/github.com/cosmos/cosmos-sdk`` like a normal Go program:
|
||||
|
||||
::
|
||||
|
||||
go get -u github.com/cosmos/cosmos-sdk
|
||||
go get github.com/cosmos/cosmos-sdk
|
||||
|
||||
If the dependencies have been updated with breaking changes, or if
|
||||
another branch is required, ``dep`` is used for dependency management.
|
||||
|
@ -20,16 +16,33 @@ repo, the correct way to install is:
|
|||
::
|
||||
|
||||
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
|
||||
git pull origin master
|
||||
make all
|
||||
make get_vendor_deps
|
||||
make install
|
||||
make install_examples
|
||||
|
||||
This will create the ``basecoin`` binary in ``$GOPATH/bin``.
|
||||
``make all`` implies ``make get_vendor_deps`` and uses ``dep`` to
|
||||
install the correct version of all dependencies. It also tests the code,
|
||||
including some cli tests to make sure your binary behaves properly.
|
||||
This will install ``gaiad`` and ``gaiacli`` and four example binaries:
|
||||
``basecoind``, ``basecli``, ``democoind``, and ``democli``.
|
||||
|
||||
If you need another branch, make sure to run ``git checkout <branch>``
|
||||
before ``make all``. And if you switch branches a lot, especially
|
||||
touching other tendermint repos, you may need to ``make fresh``
|
||||
sometimes so dep doesn't get confused with all the branches and
|
||||
versions lying around.
|
||||
Verify that everything is OK by running:
|
||||
|
||||
::
|
||||
|
||||
gaiad version
|
||||
|
||||
you should see:
|
||||
|
||||
::
|
||||
|
||||
0.15.0-rc1-9d90c6b
|
||||
|
||||
then with:
|
||||
|
||||
::
|
||||
|
||||
gaiacli version
|
||||
|
||||
you should see:
|
||||
|
||||
::
|
||||
|
||||
0.15.0-rc1-9d90c6b
|
||||
|
|
|
@ -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)
|
|
@ -547,7 +547,7 @@ components:
|
|||
properties:
|
||||
denom:
|
||||
type: string
|
||||
example: fermion
|
||||
example: steak
|
||||
amount:
|
||||
type: number
|
||||
example: 50
|
||||
|
|
|
@ -143,9 +143,6 @@ implementing the ``Msg`` interface:
|
|||
// Must be alphanumeric or empty.
|
||||
Type() string
|
||||
|
||||
// Get some property of the Msg.
|
||||
Get(key interface{}) (value interface{})
|
||||
|
||||
// Get the canonical byte representation of the Msg.
|
||||
GetSignBytes() []byte
|
||||
|
||||
|
@ -175,9 +172,6 @@ Messages can specify basic self-consistency checks using the ``ValidateBasic()``
|
|||
method to enforce that message contents are well formed before any actual logic
|
||||
begins.
|
||||
|
||||
Finally, messages can provide generic access to their contents via ``Get(key)``,
|
||||
but this is mostly for convenience and not type-safe.
|
||||
|
||||
For instance, the ``Basecoin`` message types are defined in ``x/bank/tx.go``:
|
||||
|
||||
::
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
# IBC Specification
|
||||
|
||||
IBC(Inter-Blockchain Communication) protocol is used by multiple zones on Cosmos. Using IBC, the zones can send coins or arbitrary data to other zones.
|
||||
The IBC (Inter Blockchain Communication) protocol specifies how tokens,
|
||||
non-fungible assets and complex objects can be moved securely between different
|
||||
zones (independent blockchains). IBC is conceptually similar to TCP/IP in the
|
||||
sense that anyone can implement it in order to be able to establish IBC
|
||||
connections with willing clients.
|
||||
|
||||
|
||||
## Terms
|
||||
|
||||
How IBC module treats incoming IBC packets is simillar with how BaseApp treats incoming transactions. Therefore, the components of IBC module have their corresponding pair in BaseApp.
|
||||
How IBC module treats incoming IBC packets is similar to how BaseApp treats
|
||||
incoming transactions. Therefore, the components of IBC module have their
|
||||
corresponding pair in BaseApp.
|
||||
|
||||
| BaseApp Terms | IBC Terms |
|
||||
| ------------- | ---------- |
|
||||
|
@ -12,20 +19,27 @@ How IBC module treats incoming IBC packets is simillar with how BaseApp treats i
|
|||
| Tx | Packet |
|
||||
| Msg | Payload |
|
||||
|
||||
|
||||
## MVP Specifications
|
||||
|
||||
### [MVP1](./mvp1.md)
|
||||
|
||||
MVP1 will contain the basic functionalities, including packet generation and packet receivement. There will be no security check for incoming packets.
|
||||
MVP1 will contain the basic functionalities, including packet generation and
|
||||
incoming packet processing. There will be no security check for incoming
|
||||
packets.
|
||||
|
||||
### [MVP2](./mvp2.md)
|
||||
|
||||
IBC module will be more modular in MVP2. Indivisual modules can register custom handlers to IBC module.
|
||||
The IBC module will be more modular in MVP2. Individual modules can register
|
||||
custom handlers on the IBC module.
|
||||
|
||||
### [MVP3](./mvp3.md)
|
||||
|
||||
Light client verification is added to verify the message from the other chain. Registering chains with their ROT(Root Of Trust) is needed.
|
||||
Light client verification is added to verify an IBC packet from another chain.
|
||||
Registering chains with their RoT(Root of Trust) is added as well.
|
||||
|
||||
### [MVP4](./mvp4.md)
|
||||
|
||||
ACK verification / timeout handler helper functions and messaging queue are implemented to make it failsafe. Callbacks will be registered to the dispatcher to handle failure when they register handlers.
|
||||
ACK verification / timeout handler helper functions and messaging queues are
|
||||
implemented to make it safe. Callbacks will be registered to the dispatcher to
|
||||
handle failure when they register handlers.
|
|
@ -1,77 +1,205 @@
|
|||
Using Gaia
|
||||
==========
|
||||
Using The Staking Module
|
||||
========================
|
||||
|
||||
This project is a demonstration of the Cosmos Hub with staking functionality; it is
|
||||
designed to get validator acquianted with staking concepts and procedure.
|
||||
This project is a demonstration of the Cosmos Hub staking functionality; it is
|
||||
designed to get validator acquianted with staking concepts and procedures.
|
||||
|
||||
Potential validators will be declaring their candidacy, after which users can
|
||||
delegate and, if they so wish, unbond. This can be practiced using a local or
|
||||
public testnet.
|
||||
|
||||
This example covers initial setup of a two-node testnet between a server in the cloud and a local machine. Begin this tutorial from a cloud machine that you've ``ssh``'d into.
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
The ``gaia`` tooling is an extension of the Cosmos-SDK; to install:
|
||||
The ``gaiad`` and ``gaiacli`` binaries:
|
||||
|
||||
::
|
||||
|
||||
go get github.com/cosmos/gaia
|
||||
cd $GOPATH/src/github.com/cosmos/gaia
|
||||
go get github.com/cosmos/cosmos-sdk
|
||||
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
|
||||
make get_vendor_deps
|
||||
make install
|
||||
|
||||
It has three primary commands:
|
||||
Let's jump right into it. First, we initialize some default files:
|
||||
|
||||
::
|
||||
|
||||
Available Commands:
|
||||
node The Cosmos Network delegation-game blockchain test
|
||||
rest-server REST client for gaia commands
|
||||
client Gaia light client
|
||||
gaiad init
|
||||
|
||||
version Show version info
|
||||
help Help about any command
|
||||
which will output:
|
||||
|
||||
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
|
||||
this command to either initialize a new node, or - using existing files - joining
|
||||
the testnet.
|
||||
I[03-30|11:20:13.365] Found private validator module=main path=/root/.gaiad/config/priv_validator.json
|
||||
I[03-30|11:20:13.365] Found genesis file module=main path=/root/.gaiad/config/genesis.json
|
||||
Secret phrase to access coins:
|
||||
citizen hungry tennis noise park hire glory exercise link glow dolphin labor design grit apple abandon
|
||||
|
||||
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
|
||||
for sending various transactions and other types of interaction with a running chain.
|
||||
that you've setup or joined a testnet.
|
||||
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:
|
||||
|
||||
Generating Keys
|
||||
---------------
|
||||
::
|
||||
|
||||
Review the `key management tutorial <../key-management.html>`__ and create one key
|
||||
if you'll be joining the public testnet, and three keys if you'll be trying out a local
|
||||
testnet.
|
||||
gaiacli keys add alice --recover
|
||||
|
||||
which will give you three prompts:
|
||||
|
||||
::
|
||||
|
||||
Enter a passphrase for your key:
|
||||
Repeat the passphrase:
|
||||
Enter your recovery seed phrase:
|
||||
|
||||
create a password and copy in your seed phrase. The name and address of the key will be output:
|
||||
|
||||
::
|
||||
NAME: ADDRESS: PUBKEY:
|
||||
alice 67997DD03D527EB439B7193F2B813B05B219CC02 1624DE6220BB89786C1D597050438C728202436552C6226AB67453CDB2A4D2703402FB52B6
|
||||
|
||||
You can see all available keys with:
|
||||
|
||||
::
|
||||
|
||||
gaiacli keys list
|
||||
|
||||
Setup Testnet
|
||||
-------------
|
||||
|
||||
The first thing you'll want to do is either `create a local testnet <./local-testnet.html>`__ or
|
||||
join a `public testnet <./public-testnet.html>`__. Either step is required before proceeding.
|
||||
Next, we start the daemon (do this in another window):
|
||||
|
||||
The rest of this tutorial will assume a local testnet with three participants: ``alice`` will be
|
||||
the initial validator, ``bob`` will first receives tokens from ``alice`` then declare candidacy
|
||||
as a validator, and ``charlie`` will bond then unbond to ``bob``. If you're joining the public
|
||||
testnet, the token amounts will need to be adjusted.
|
||||
::
|
||||
|
||||
gaiad start
|
||||
|
||||
and you'll see blocks start streaming through.
|
||||
|
||||
For this example, we're doing the above on a cloud machine. The next steps should be done on your local machine or another server in the cloud, which will join the running testnet then bond/unbond.
|
||||
|
||||
Accounts
|
||||
--------
|
||||
|
||||
We have:
|
||||
|
||||
- ``alice`` the initial validator (in the cloud)
|
||||
- ``bob`` receives tokens from ``alice`` then declares candidacy (from local machine)
|
||||
- ``charlie`` will bond and unbond to ``bob`` (from local machine)
|
||||
|
||||
Remember that ``alice`` was already created. On your second machine, install the binaries and create two new keys:
|
||||
|
||||
::
|
||||
|
||||
gaiacli keys add bob
|
||||
gaiacli keys add charlie
|
||||
|
||||
both of which will prompt you for a password. Now we need to copy the ``genesis.json`` and ``config.toml`` from the first machine (with ``alice``) to the second machine. This is a good time to look at both these files.
|
||||
|
||||
The ``genesis.json`` should look something like:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"app_state": {
|
||||
"accounts": [
|
||||
{
|
||||
"address": "1D9B2356CAADF46D3EE3488E3CCE3028B4283DEE",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "steak",
|
||||
"amount": 100000
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"stake": {
|
||||
"pool": {
|
||||
"total_supply": 0,
|
||||
"bonded_shares": {
|
||||
"num": 0,
|
||||
"denom": 1
|
||||
},
|
||||
"unbonded_shares": {
|
||||
"num": 0,
|
||||
"denom": 1
|
||||
},
|
||||
"bonded_pool": 0,
|
||||
"unbonded_pool": 0,
|
||||
"inflation_last_time": 0,
|
||||
"inflation": {
|
||||
"num": 7,
|
||||
"denom": 100
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"inflation_rate_change": {
|
||||
"num": 13,
|
||||
"denom": 100
|
||||
},
|
||||
"inflation_max": {
|
||||
"num": 20,
|
||||
"denom": 100
|
||||
},
|
||||
"inflation_min": {
|
||||
"num": 7,
|
||||
"denom": 100
|
||||
},
|
||||
"goal_bonded": {
|
||||
"num": 67,
|
||||
"denom": 100
|
||||
},
|
||||
"max_validators": 100,
|
||||
"bond_denom": "steak"
|
||||
}
|
||||
}
|
||||
},
|
||||
"validators": [
|
||||
{
|
||||
"pub_key": {
|
||||
"type": "AC26791624DE60",
|
||||
"value": "rgpc/ctVld6RpSfwN5yxGBF17R1PwMTdhQ9gKVUZp5g="
|
||||
},
|
||||
"power": 10,
|
||||
"name": ""
|
||||
}
|
||||
],
|
||||
"app_hash": "",
|
||||
"genesis_time": "0001-01-01T00:00:00Z",
|
||||
"chain_id": "test-chain-Uv1EVU"
|
||||
}
|
||||
|
||||
|
||||
To notice is that the ``accounts`` field has a an address and a whole bunch of "mycoin". This is ``alice``'s address (todo: dbl check). Under ``validators`` we see the ``pub_key.data`` field, which will match the same field in the ``priv_validator.json`` file.
|
||||
|
||||
The ``config.toml`` is long so let's focus on one field:
|
||||
|
||||
::
|
||||
|
||||
# Comma separated list of seed nodes to connect to
|
||||
seeds = ""
|
||||
|
||||
On the ``alice`` cloud machine, we don't need to do anything here. Instead, we need its IP address. After copying this file (and the ``genesis.json`` to your local machine, you'll want to put the IP in the ``seeds = "138.197.161.74"`` field, in this case, we have a made-up IP. For joining testnets with many nodes, you can add more comma-seperated IPs to the list.
|
||||
|
||||
|
||||
Now that your files are all setup, it's time to join the network. On your local machine, run:
|
||||
|
||||
::
|
||||
|
||||
gaiad start
|
||||
|
||||
and your new node will connect to the running validator (``alice``).
|
||||
|
||||
Sending Tokens
|
||||
--------------
|
||||
|
||||
We'll have ``alice`` who is currently quite rich, send some ``fermions`` to ``bob``:
|
||||
We'll have ``alice`` send some ``mycoin`` to ``bob``, who has now joined the network:
|
||||
|
||||
::
|
||||
|
||||
gaia client tx send --amount=1000fermion --sequence=1 --name=alice --to=5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6
|
||||
gaiacli send --amount=1000mycoin --sequence=0 --name=alice --to=5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6 --chain-id=test-chain-Uv1EVU
|
||||
|
||||
where the ``--sequence`` flag is to be incremented for each transaction, the ``--name`` flag names the sender, and the ``--to`` flag takes ``bob``'s address. You'll see something like:
|
||||
where the ``--sequence`` flag is to be incremented for each transaction, the ``--name`` flag is the sender (alice), and the ``--to`` flag takes ``bob``'s address. You'll see something like:
|
||||
|
||||
::
|
||||
|
||||
|
@ -101,19 +229,25 @@ where the ``--sequence`` flag is to be incremented for each transaction, the ``-
|
|||
"height": 2963
|
||||
}
|
||||
|
||||
Check out ``bob``'s account, which should now have 992 fermions:
|
||||
TODO: check the above with current actual output.
|
||||
|
||||
Check out ``bob``'s account, which should now have 1000 mycoin:
|
||||
|
||||
::
|
||||
|
||||
gaia client query account 5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6
|
||||
gaiacli account 5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6
|
||||
|
||||
Adding a Second Validator
|
||||
-------------------------
|
||||
|
||||
**This section is wrong/needs to be updated**
|
||||
|
||||
Next, let's add the second node as a validator.
|
||||
|
||||
First, we need the pub_key data:
|
||||
|
||||
** need to make bob a priv_Val above?
|
||||
|
||||
::
|
||||
|
||||
cat $HOME/.gaia2/priv_validator.json
|
||||
|
@ -130,7 +264,7 @@ Now ``bob`` can declare candidacy to that pubkey:
|
|||
|
||||
::
|
||||
|
||||
gaia client tx declare-candidacy --amount=10fermion --name=bob --pubkey=<pub_key data> --moniker=bobby
|
||||
gaiacli declare-candidacy --amount=10mycoin --name=bob --pubkey=<pub_key data> --moniker=bobby
|
||||
|
||||
with an output like:
|
||||
|
||||
|
@ -147,11 +281,11 @@ with an output like:
|
|||
}
|
||||
|
||||
|
||||
We should see ``bob``'s account balance decrease by 10 fermions:
|
||||
We should see ``bob``'s account balance decrease by 10 mycoin:
|
||||
|
||||
::
|
||||
|
||||
gaia client query account 5D93A6059B6592833CBC8FA3DA90EE0382198985
|
||||
gaiacli account 5D93A6059B6592833CBC8FA3DA90EE0382198985
|
||||
|
||||
To confirm for certain the new validator is active, ask the tendermint node:
|
||||
|
||||
|
@ -163,7 +297,7 @@ If you now kill either node, blocks will stop streaming in, because
|
|||
there aren't enough validators online. Turn it back on and they will
|
||||
start streaming again.
|
||||
|
||||
Now that ``bob`` has declared candidacy, which essentially bonded 10 fermions and made him a validator, we're going to get ``charlie`` to delegate some coins to ``bob``.
|
||||
Now that ``bob`` has declared candidacy, which essentially bonded 10 mycoin and made him a validator, we're going to get ``charlie`` to delegate some coins to ``bob``.
|
||||
|
||||
Delegating
|
||||
----------
|
||||
|
@ -172,13 +306,13 @@ First let's have ``alice`` send some coins to ``charlie``:
|
|||
|
||||
::
|
||||
|
||||
gaia client tx send --amount=1000fermion --sequence=2 --name=alice --to=48F74F48281C89E5E4BE9092F735EA519768E8EF
|
||||
gaiacli tx --amount=1000mycoin --sequence=2 --name=alice --to=48F74F48281C89E5E4BE9092F735EA519768E8EF
|
||||
|
||||
Then ``charlie`` will delegate some fermions to ``bob``:
|
||||
Then ``charlie`` will delegate some mycoin to ``bob``:
|
||||
|
||||
::
|
||||
|
||||
gaia client tx delegate --amount=10fermion --name=charlie --pubkey=<pub_key data>
|
||||
gaiacli tx delegate --amount=10mycoin --name=charlie --pubkey=<pub_key data>
|
||||
|
||||
You'll see output like:
|
||||
|
||||
|
@ -194,13 +328,13 @@ You'll see output like:
|
|||
"height": 51585
|
||||
}
|
||||
|
||||
And that's it. You can query ``charlie``'s account to see the decrease in fermions.
|
||||
And that's it. You can query ``charlie``'s account to see the decrease in mycoin.
|
||||
|
||||
To get more information about the candidate, try:
|
||||
|
||||
::
|
||||
|
||||
gaia client query candidate --pubkey=<pub_key data>
|
||||
gaiacli query candidate --pubkey=<pub_key data>
|
||||
|
||||
and you'll see output similar to:
|
||||
|
||||
|
@ -233,7 +367,7 @@ It's also possible the query the delegator's bond like so:
|
|||
|
||||
::
|
||||
|
||||
gaia client query delegator-bond --delegator-address 48F74F48281C89E5E4BE9092F735EA519768E8EF --pubkey 52D6FCD8C92A97F7CCB01205ADF310A18411EA8FDCC10E65BF2FCDB05AD1689B
|
||||
gaiacli query delegator-bond --delegator-address 48F74F48281C89E5E4BE9092F735EA519768E8EF --pubkey 52D6FCD8C92A97F7CCB01205ADF310A18411EA8FDCC10E65BF2FCDB05AD1689B
|
||||
|
||||
with an output similar to:
|
||||
|
||||
|
@ -262,9 +396,7 @@ your VotingPower reduce and your account balance increase.
|
|||
|
||||
::
|
||||
|
||||
gaia client tx unbond --amount=5fermion --name=charlie --pubkey=<pub_key data>
|
||||
gaia client query account 48F74F48281C89E5E4BE9092F735EA519768E8EF
|
||||
gaiacli unbond --amount=5mycoin --name=charlie --pubkey=<pub_key data>
|
||||
gaiacli account 48F74F48281C89E5E4BE9092F735EA519768E8EF
|
||||
|
||||
See the bond decrease with ``gaia client query delegator-bond`` like above.
|
||||
|
||||
That concludes an overview of the ``gaia`` tooling for local testing.
|
||||
See the bond decrease with ``gaiacli query delegator-bond`` like above.
|
||||
|
|
|
@ -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
|
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/x/simplestake"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
||||
)
|
||||
|
@ -29,16 +29,16 @@ type BasecoinApp struct {
|
|||
cdc *wire.Codec
|
||||
|
||||
// keys to access the substores
|
||||
capKeyMainStore *sdk.KVStoreKey
|
||||
capKeyAccountStore *sdk.KVStoreKey
|
||||
capKeyIBCStore *sdk.KVStoreKey
|
||||
capKeyStakingStore *sdk.KVStoreKey
|
||||
keyMain *sdk.KVStoreKey
|
||||
keyAccount *sdk.KVStoreKey
|
||||
keyIBC *sdk.KVStoreKey
|
||||
keyStake *sdk.KVStoreKey
|
||||
|
||||
// Manage getting and setting accounts
|
||||
accountMapper sdk.AccountMapper
|
||||
|
||||
// Handle fees
|
||||
feeHandler sdk.FeeHandler
|
||||
coinKeeper bank.Keeper
|
||||
ibcMapper ibc.Mapper
|
||||
stakeKeeper stake.Keeper
|
||||
}
|
||||
|
||||
func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
||||
|
@ -48,92 +48,64 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
|||
|
||||
// Create your application object.
|
||||
var app = &BasecoinApp{
|
||||
BaseApp: bam.NewBaseApp(appName, logger, db),
|
||||
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
|
||||
cdc: cdc,
|
||||
capKeyMainStore: sdk.NewKVStoreKey("main"),
|
||||
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
|
||||
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
|
||||
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
|
||||
keyMain: sdk.NewKVStoreKey("main"),
|
||||
keyAccount: sdk.NewKVStoreKey("acc"),
|
||||
keyIBC: sdk.NewKVStoreKey("ibc"),
|
||||
keyStake: sdk.NewKVStoreKey("stake"),
|
||||
}
|
||||
|
||||
// Define the accountMapper.
|
||||
app.accountMapper = auth.NewAccountMapper(
|
||||
cdc,
|
||||
app.capKeyMainStore, // target store
|
||||
app.keyAccount, // target store
|
||||
&types.AppAccount{}, // prototype
|
||||
).Seal()
|
||||
)
|
||||
|
||||
// Add handlers.
|
||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
||||
// add accountMapper/handlers
|
||||
app.coinKeeper = bank.NewKeeper(app.accountMapper)
|
||||
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
|
||||
|
||||
// register message routes
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
||||
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
|
||||
|
||||
// Define the feeHandler.
|
||||
app.feeHandler = auth.BurnFeeHandler
|
||||
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
|
||||
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
|
||||
AddRoute("stake", stake.NewHandler(app.stakeKeeper))
|
||||
|
||||
// Initialize BaseApp.
|
||||
app.SetTxDecoder(app.txDecoder)
|
||||
app.SetInitChainer(app.initChainer)
|
||||
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeyIBCStore, app.capKeyStakingStore)
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeHandler))
|
||||
err := app.LoadLatestVersion(app.capKeyMainStore)
|
||||
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake)
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, auth.BurnFeeHandler))
|
||||
err := app.LoadLatestVersion(app.keyMain)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// Custom tx codec
|
||||
func MakeCodec() *wire.Codec {
|
||||
var cdc = wire.NewCodec()
|
||||
wire.RegisterCrypto(cdc) // Register crypto.
|
||||
sdk.RegisterWire(cdc) // Register Msgs
|
||||
bank.RegisterWire(cdc)
|
||||
stake.RegisterWire(cdc)
|
||||
ibc.RegisterWire(cdc)
|
||||
|
||||
// Register Msgs
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(bank.SendMsg{}, "basecoin/Send", nil)
|
||||
cdc.RegisterConcrete(bank.IssueMsg{}, "basecoin/Issue", nil)
|
||||
cdc.RegisterConcrete(ibc.IBCTransferMsg{}, "basecoin/IBCTransferMsg", nil)
|
||||
cdc.RegisterConcrete(ibc.IBCReceiveMsg{}, "basecoin/IBCReceiveMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.BondMsg{}, "basecoin/BondMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.UnbondMsg{}, "basecoin/UnbondMsg", nil)
|
||||
|
||||
// Register AppAccount
|
||||
// register custom AppAccount
|
||||
cdc.RegisterInterface((*sdk.Account)(nil), nil)
|
||||
cdc.RegisterConcrete(&types.AppAccount{}, "basecoin/Account", nil)
|
||||
|
||||
// Register crypto.
|
||||
wire.RegisterCrypto(cdc)
|
||||
|
||||
return cdc
|
||||
}
|
||||
|
||||
// Custom logic for transaction decoding
|
||||
func (app *BasecoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||
var tx = sdk.StdTx{}
|
||||
|
||||
if len(txBytes) == 0 {
|
||||
return nil, sdk.ErrTxDecode("txBytes are empty")
|
||||
}
|
||||
|
||||
// StdTx.Msg is an interface. The concrete types
|
||||
// are registered by MakeTxCodec in bank.RegisterAmino.
|
||||
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
|
||||
}
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
// Custom logic for basecoin initialization
|
||||
func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
stateJSON := req.AppStateBytes
|
||||
|
||||
genesisState := new(types.GenesisState)
|
||||
err := json.Unmarshal(stateJSON, genesisState)
|
||||
err := app.cdc.UnmarshalJSON(stateJSON, genesisState)
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
|
@ -149,3 +121,25 @@ func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain)
|
|||
}
|
||||
return abci.ResponseInitChain{}
|
||||
}
|
||||
|
||||
// Custom logic for state export
|
||||
func (app *BasecoinApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
|
||||
ctx := app.NewContext(true, abci.Header{})
|
||||
|
||||
// iterate to get the accounts
|
||||
accounts := []*types.GenesisAccount{}
|
||||
appendAccount := func(acc sdk.Account) (stop bool) {
|
||||
account := &types.GenesisAccount{
|
||||
Address: acc.GetAddress(),
|
||||
Coins: acc.GetCoins(),
|
||||
}
|
||||
accounts = append(accounts, account)
|
||||
return false
|
||||
}
|
||||
app.accountMapper.IterateAccounts(ctx, appendAccount)
|
||||
|
||||
genState := types.GenesisState{
|
||||
Accounts: accounts,
|
||||
}
|
||||
return wire.MarshalJSONIndent(app.cdc, genState)
|
||||
}
|
||||
|
|
|
@ -42,12 +42,12 @@ var (
|
|||
0,
|
||||
}
|
||||
|
||||
sendMsg1 = bank.SendMsg{
|
||||
sendMsg1 = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
|
||||
}
|
||||
|
||||
sendMsg2 = bank.SendMsg{
|
||||
sendMsg2 = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{
|
||||
bank.NewOutput(addr2, halfCoins),
|
||||
|
@ -55,7 +55,7 @@ var (
|
|||
},
|
||||
}
|
||||
|
||||
sendMsg3 = bank.SendMsg{
|
||||
sendMsg3 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr1, coins),
|
||||
bank.NewInput(addr4, coins),
|
||||
|
@ -66,7 +66,7 @@ var (
|
|||
},
|
||||
}
|
||||
|
||||
sendMsg4 = bank.SendMsg{
|
||||
sendMsg4 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr2, coins),
|
||||
},
|
||||
|
@ -75,7 +75,7 @@ var (
|
|||
},
|
||||
}
|
||||
|
||||
sendMsg5 = bank.SendMsg{
|
||||
sendMsg5 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr1, manyCoins),
|
||||
},
|
||||
|
@ -166,7 +166,7 @@ func TestSortGenesis(t *testing.T) {
|
|||
|
||||
// Unsorted coins means invalid
|
||||
err := sendMsg5.ValidateBasic()
|
||||
require.Equal(t, sdk.CodeInvalidCoins, err.ABCICode(), err.ABCILog())
|
||||
require.Equal(t, sdk.CodeInvalidCoins, err.Code(), err.ABCILog())
|
||||
|
||||
// Sort coins, should be valid
|
||||
sendMsg5.Inputs[0].Coins.Sort()
|
||||
|
@ -208,7 +208,7 @@ func TestGenesis(t *testing.T) {
|
|||
assert.Equal(t, acc, res1)
|
||||
}
|
||||
|
||||
func TestSendMsgWithAccounts(t *testing.T) {
|
||||
func TestMsgSendWithAccounts(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
// Construct some genesis bytes to reflect basecoin/types/AppAccount
|
||||
|
@ -243,13 +243,13 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
|||
tx.Signatures[0].Sequence = 1
|
||||
res := bapp.Deliver(tx)
|
||||
|
||||
assert.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
// resigning the tx with the bumped sequence should work
|
||||
SignCheckDeliver(t, bapp, sendMsg1, []int64{1}, true, priv1)
|
||||
}
|
||||
|
||||
func TestSendMsgMultipleOut(t *testing.T) {
|
||||
func TestMsgSendMultipleOut(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
genCoins, err := sdk.ParseCoins("42foocoin")
|
||||
|
@ -311,7 +311,7 @@ func TestSengMsgMultipleInOut(t *testing.T) {
|
|||
CheckBalance(t, bapp, addr3, "10foocoin")
|
||||
}
|
||||
|
||||
func TestSendMsgDependent(t *testing.T) {
|
||||
func TestMsgSendDependent(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
genCoins, err := sdk.ParseCoins("42foocoin")
|
||||
|
@ -339,7 +339,7 @@ func TestSendMsgDependent(t *testing.T) {
|
|||
CheckBalance(t, bapp, addr1, "42foocoin")
|
||||
}
|
||||
|
||||
func TestQuizMsg(t *testing.T) {
|
||||
func TestMsgQuiz(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
// Construct genesis state
|
||||
|
@ -437,18 +437,18 @@ func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq []int64,
|
|||
// Run a Check
|
||||
res := bapp.Check(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
// Simulate a Block
|
||||
bapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = bapp.Deliver(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
bapp.EndBlock(abci.RequestEndBlock{})
|
||||
//bapp.Commit()
|
||||
|
|
|
@ -14,10 +14,10 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
|
||||
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands"
|
||||
simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
|
||||
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli"
|
||||
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
||||
|
@ -51,24 +51,18 @@ func main() {
|
|||
// add query/post commands (custom to binary)
|
||||
rootCmd.AddCommand(
|
||||
client.GetCommands(
|
||||
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
|
||||
authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)),
|
||||
)...)
|
||||
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
bankcmd.SendTxCmd(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCTransferCmd(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCRelayCmd(cdc),
|
||||
simplestakingcmd.BondTxCmd(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
simplestakingcmd.UnbondTxCmd(cdc),
|
||||
stakecmd.GetCmdDeclareCandidacy(cdc),
|
||||
stakecmd.GetCmdEditCandidacy(cdc),
|
||||
stakecmd.GetCmdDelegate(cdc),
|
||||
stakecmd.GetCmdUnbond(cdc),
|
||||
)...)
|
||||
|
||||
// add proxy, version and key info
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
|
@ -15,31 +15,31 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/server"
|
||||
)
|
||||
|
||||
// rootCmd is the entry point for this binary
|
||||
var (
|
||||
context = server.NewDefaultContext()
|
||||
rootCmd = &cobra.Command{
|
||||
func main() {
|
||||
cdc := app.MakeCodec()
|
||||
ctx := server.NewDefaultContext()
|
||||
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "basecoind",
|
||||
Short: "Basecoin Daemon (server)",
|
||||
PersistentPreRunE: server.PersistentPreRunEFn(context),
|
||||
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
|
||||
}
|
||||
)
|
||||
|
||||
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||
dataDir := filepath.Join(rootDir, "data")
|
||||
db, err := dbm.NewGoLevelDB("basecoin", dataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bapp := app.NewBasecoinApp(logger, db)
|
||||
return bapp, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context)
|
||||
server.AddCommands(ctx, cdc, rootCmd, server.DefaultAppInit,
|
||||
server.ConstructAppCreator(newApp, "basecoin"),
|
||||
server.ConstructAppExporter(exportAppState, "basecoin"))
|
||||
|
||||
// prepare and add flags
|
||||
rootDir := os.ExpandEnv("$HOME/.basecoind")
|
||||
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
|
||||
executor.Execute()
|
||||
}
|
||||
|
||||
func newApp(logger log.Logger, db dbm.DB) abci.Application {
|
||||
return app.NewBasecoinApp(logger, db)
|
||||
}
|
||||
|
||||
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) {
|
||||
bapp := app.NewBasecoinApp(logger, db)
|
||||
return bapp.ExportAppStateJSON()
|
||||
}
|
||||
|
|
|
@ -14,11 +14,11 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/x/simplestake"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/simplestake"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/sketchy"
|
||||
)
|
||||
|
||||
|
@ -38,11 +38,15 @@ type DemocoinApp struct {
|
|||
capKeyIBCStore *sdk.KVStoreKey
|
||||
capKeyStakingStore *sdk.KVStoreKey
|
||||
|
||||
// keepers
|
||||
coinKeeper bank.Keeper
|
||||
coolKeeper cool.Keeper
|
||||
powKeeper pow.Keeper
|
||||
ibcMapper ibc.Mapper
|
||||
stakeKeeper simplestake.Keeper
|
||||
|
||||
// Manage getting and setting accounts
|
||||
accountMapper sdk.AccountMapper
|
||||
|
||||
// Handle fees
|
||||
feeHandler sdk.FeeHandler
|
||||
}
|
||||
|
||||
func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
|
||||
|
@ -52,7 +56,7 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
|
|||
|
||||
// Create your application object.
|
||||
var app = &DemocoinApp{
|
||||
BaseApp: bam.NewBaseApp(appName, logger, db),
|
||||
BaseApp: bam.NewBaseApp(appName, cdc, logger, db),
|
||||
cdc: cdc,
|
||||
capKeyMainStore: sdk.NewKVStoreKey("main"),
|
||||
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
|
||||
|
@ -64,90 +68,59 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
|
|||
// Define the accountMapper.
|
||||
app.accountMapper = auth.NewAccountMapper(
|
||||
cdc,
|
||||
app.capKeyMainStore, // target store
|
||||
app.capKeyAccountStore, // target store
|
||||
&types.AppAccount{}, // prototype
|
||||
).Seal()
|
||||
)
|
||||
|
||||
// Add handlers.
|
||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
||||
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
|
||||
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper)
|
||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
||||
app.coinKeeper = bank.NewKeeper(app.accountMapper)
|
||||
app.coolKeeper = cool.NewKeeper(app.capKeyMainStore, app.coinKeeper, app.RegisterCodespace(cool.DefaultCodespace))
|
||||
app.powKeeper = pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), app.coinKeeper, app.RegisterCodespace(pow.DefaultCodespace))
|
||||
app.ibcMapper = ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||
app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace))
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||
AddRoute("cool", cool.NewHandler(coolKeeper)).
|
||||
AddRoute("pow", powKeeper.Handler).
|
||||
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
|
||||
AddRoute("cool", cool.NewHandler(app.coolKeeper)).
|
||||
AddRoute("pow", app.powKeeper.Handler).
|
||||
AddRoute("sketchy", sketchy.NewHandler()).
|
||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
||||
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
|
||||
|
||||
// Define the feeHandler.
|
||||
app.feeHandler = auth.BurnFeeHandler
|
||||
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
|
||||
AddRoute("simplestake", simplestake.NewHandler(app.stakeKeeper))
|
||||
|
||||
// Initialize BaseApp.
|
||||
app.SetTxDecoder(app.txDecoder)
|
||||
app.SetInitChainer(app.initChainerFn(coolKeeper, powKeeper))
|
||||
app.SetInitChainer(app.initChainerFn(app.coolKeeper, app.powKeeper))
|
||||
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyAccountStore, app.capKeyPowStore, app.capKeyIBCStore, app.capKeyStakingStore)
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeHandler))
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, auth.BurnFeeHandler))
|
||||
err := app.LoadLatestVersion(app.capKeyMainStore)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// custom tx codec
|
||||
func MakeCodec() *wire.Codec {
|
||||
var cdc = wire.NewCodec()
|
||||
|
||||
// Register Msgs
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(bank.SendMsg{}, "democoin/Send", nil)
|
||||
cdc.RegisterConcrete(bank.IssueMsg{}, "democoin/Issue", nil)
|
||||
cdc.RegisterConcrete(cool.QuizMsg{}, "democoin/Quiz", nil)
|
||||
cdc.RegisterConcrete(cool.SetTrendMsg{}, "democoin/SetTrend", nil)
|
||||
cdc.RegisterConcrete(pow.MineMsg{}, "democoin/Mine", nil)
|
||||
cdc.RegisterConcrete(ibc.IBCTransferMsg{}, "democoin/IBCTransferMsg", nil)
|
||||
cdc.RegisterConcrete(ibc.IBCReceiveMsg{}, "democoin/IBCReceiveMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.BondMsg{}, "democoin/BondMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.UnbondMsg{}, "democoin/UnbondMsg", nil)
|
||||
wire.RegisterCrypto(cdc) // Register crypto.
|
||||
sdk.RegisterWire(cdc) // Register Msgs
|
||||
cool.RegisterWire(cdc)
|
||||
pow.RegisterWire(cdc)
|
||||
bank.RegisterWire(cdc)
|
||||
ibc.RegisterWire(cdc)
|
||||
simplestake.RegisterWire(cdc)
|
||||
|
||||
// Register AppAccount
|
||||
cdc.RegisterInterface((*sdk.Account)(nil), nil)
|
||||
cdc.RegisterConcrete(&types.AppAccount{}, "democoin/Account", nil)
|
||||
|
||||
// Register crypto.
|
||||
wire.RegisterCrypto(cdc)
|
||||
|
||||
return cdc
|
||||
}
|
||||
|
||||
// custom logic for transaction decoding
|
||||
func (app *DemocoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||
var tx = sdk.StdTx{}
|
||||
|
||||
if len(txBytes) == 0 {
|
||||
return nil, sdk.ErrTxDecode("txBytes are empty")
|
||||
}
|
||||
|
||||
// StdTx.Msg is an interface. The concrete types
|
||||
// are registered by MakeTxCodec in bank.RegisterWire.
|
||||
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
|
||||
}
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
// custom logic for democoin initialization
|
||||
func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
stateJSON := req.AppStateBytes
|
||||
|
||||
genesisState := new(types.GenesisState)
|
||||
err := json.Unmarshal(stateJSON, genesisState)
|
||||
err := app.cdc.UnmarshalJSON(stateJSON, genesisState)
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
|
@ -163,13 +136,13 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep
|
|||
}
|
||||
|
||||
// Application specific genesis handling
|
||||
err = coolKeeper.InitGenesis(ctx, genesisState.CoolGenesis)
|
||||
err = cool.InitGenesis(ctx, app.coolKeeper, genesisState.CoolGenesis)
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
}
|
||||
|
||||
err = powKeeper.InitGenesis(ctx, genesisState.PowGenesis)
|
||||
err = pow.InitGenesis(ctx, app.powKeeper, genesisState.POWGenesis)
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
|
@ -178,3 +151,27 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep
|
|||
return abci.ResponseInitChain{}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom logic for state export
|
||||
func (app *DemocoinApp) ExportAppStateJSON() (appState json.RawMessage, err error) {
|
||||
ctx := app.NewContext(true, abci.Header{})
|
||||
|
||||
// iterate to get the accounts
|
||||
accounts := []*types.GenesisAccount{}
|
||||
appendAccount := func(acc sdk.Account) (stop bool) {
|
||||
account := &types.GenesisAccount{
|
||||
Address: acc.GetAddress(),
|
||||
Coins: acc.GetCoins(),
|
||||
}
|
||||
accounts = append(accounts, account)
|
||||
return false
|
||||
}
|
||||
app.accountMapper.IterateAccounts(ctx, appendAccount)
|
||||
|
||||
genState := types.GenesisState{
|
||||
Accounts: accounts,
|
||||
POWGenesis: pow.WriteGenesis(ctx, app.powKeeper),
|
||||
CoolGenesis: cool.WriteGenesis(ctx, app.coolKeeper),
|
||||
}
|
||||
return wire.MarshalJSONIndent(app.cdc, genState)
|
||||
}
|
||||
|
|
|
@ -36,32 +36,32 @@ var (
|
|||
0,
|
||||
}
|
||||
|
||||
sendMsg = bank.SendMsg{
|
||||
sendMsg = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
|
||||
}
|
||||
|
||||
quizMsg1 = cool.QuizMsg{
|
||||
quizMsg1 = cool.MsgQuiz{
|
||||
Sender: addr1,
|
||||
CoolAnswer: "icecold",
|
||||
}
|
||||
|
||||
quizMsg2 = cool.QuizMsg{
|
||||
quizMsg2 = cool.MsgQuiz{
|
||||
Sender: addr1,
|
||||
CoolAnswer: "badvibesonly",
|
||||
}
|
||||
|
||||
setTrendMsg1 = cool.SetTrendMsg{
|
||||
setTrendMsg1 = cool.MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "icecold",
|
||||
}
|
||||
|
||||
setTrendMsg2 = cool.SetTrendMsg{
|
||||
setTrendMsg2 = cool.MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "badvibesonly",
|
||||
}
|
||||
|
||||
setTrendMsg3 = cool.SetTrendMsg{
|
||||
setTrendMsg3 = cool.MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "warmandkind",
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ func TestGenesis(t *testing.T) {
|
|||
assert.Equal(t, acc, res1)
|
||||
}
|
||||
|
||||
func TestSendMsgWithAccounts(t *testing.T) {
|
||||
func TestMsgSendWithAccounts(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
// Construct some genesis bytes to reflect democoin/types/AppAccount
|
||||
|
@ -202,12 +202,12 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
|||
|
||||
// Run a Check
|
||||
res := bapp.Check(tx)
|
||||
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
|
||||
// Simulate a Block
|
||||
bapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
|
||||
// Check balances
|
||||
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
|
||||
|
@ -218,22 +218,22 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
|||
|
||||
// Delivering again should cause replay error
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.CodeInvalidSequence, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeInvalidSequence), sdk.ABCICodeType(res.Code), res.Log)
|
||||
|
||||
// bumping the txnonce number without resigning should be an auth error
|
||||
tx.Signatures[0].Sequence = 1
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), sdk.ABCICodeType(res.Code), res.Log)
|
||||
|
||||
// resigning the tx with the bumped sequence should work
|
||||
sequences = []int64{1}
|
||||
sig = priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, tx.Msg))
|
||||
tx.Signatures[0].Signature = sig
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
func TestMineMsg(t *testing.T) {
|
||||
func TestMsgMine(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
// Construct genesis state
|
||||
|
@ -271,11 +271,11 @@ func TestMineMsg(t *testing.T) {
|
|||
assert.Equal(t, acc1, res1)
|
||||
|
||||
// Mine and check for reward
|
||||
mineMsg1 := pow.GenerateMineMsg(addr1, 1, 2)
|
||||
mineMsg1 := pow.GenerateMsgMine(addr1, 1, 2)
|
||||
SignCheckDeliver(t, bapp, mineMsg1, 0, true)
|
||||
CheckBalance(t, bapp, "1pow")
|
||||
// Mine again and check for reward
|
||||
mineMsg2 := pow.GenerateMineMsg(addr1, 2, 3)
|
||||
mineMsg2 := pow.GenerateMsgMine(addr1, 2, 3)
|
||||
SignCheckDeliver(t, bapp, mineMsg2, 1, true)
|
||||
CheckBalance(t, bapp, "2pow")
|
||||
// Mine again - should be invalid
|
||||
|
@ -284,7 +284,7 @@ func TestMineMsg(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestQuizMsg(t *testing.T) {
|
||||
func TestMsgQuiz(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
// Construct genesis state
|
||||
|
@ -403,18 +403,18 @@ func SignCheckDeliver(t *testing.T, bapp *DemocoinApp, msg sdk.Msg, seq int64, e
|
|||
// Run a Check
|
||||
res := bapp.Check(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
// Simulate a Block
|
||||
bapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = bapp.Deliver(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
bapp.EndBlock(abci.RequestEndBlock{})
|
||||
//bapp.Commit()
|
||||
|
|
|
@ -14,15 +14,15 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
|
||||
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands"
|
||||
simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
|
||||
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
||||
coolcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool/commands"
|
||||
powcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/pow/commands"
|
||||
coolcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool/client/cli"
|
||||
powcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/pow/client/cli"
|
||||
simplestakingcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/simplestake/client/cli"
|
||||
)
|
||||
|
||||
// rootCmd is the entry point for this binary
|
||||
|
@ -54,7 +54,7 @@ func main() {
|
|||
// start with commands common to basecoin
|
||||
rootCmd.AddCommand(
|
||||
client.GetCommands(
|
||||
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
|
||||
authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
|
|
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
|
@ -14,49 +13,57 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
// rootCmd is the entry point for this binary
|
||||
var (
|
||||
context = server.NewDefaultContext()
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "democoind",
|
||||
Short: "Democoin Daemon (server)",
|
||||
PersistentPreRunE: server.PersistentPreRunEFn(context),
|
||||
}
|
||||
)
|
||||
|
||||
// defaultAppState sets up the app_state for the
|
||||
// default genesis file
|
||||
func defaultAppState(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
|
||||
baseJSON, err := server.DefaultGenAppState(args, addr, coinDenom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var jsonMap map[string]json.RawMessage
|
||||
err = json.Unmarshal(baseJSON, &jsonMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonMap["cool"] = json.RawMessage(`{
|
||||
"trend": "ice-cold"
|
||||
}`)
|
||||
bz, err := json.Marshal(jsonMap)
|
||||
return json.RawMessage(bz), err
|
||||
// init parameters
|
||||
var CoolAppInit = server.AppInit{
|
||||
AppGenState: CoolAppGenState,
|
||||
AppGenTx: server.SimpleAppGenTx,
|
||||
}
|
||||
|
||||
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||
db, err := dbm.NewGoLevelDB("democoin", filepath.Join(rootDir, "data"))
|
||||
// coolGenAppParams sets up the app_state and appends the cool app state
|
||||
func CoolAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState json.RawMessage, err error) {
|
||||
appState, err = server.SimpleAppGenState(cdc, appGenTxs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
bapp := app.NewDemocoinApp(logger, db)
|
||||
return bapp, nil
|
||||
key := "cool"
|
||||
value := json.RawMessage(`{
|
||||
"trend": "ice-cold"
|
||||
}`)
|
||||
appState, err = server.AppendJSON(cdc, appState, key, value)
|
||||
key = "pow"
|
||||
value = json.RawMessage(`{
|
||||
"difficulty": 1,
|
||||
"count": 0
|
||||
}`)
|
||||
appState, err = server.AppendJSON(cdc, appState, key, value)
|
||||
return
|
||||
}
|
||||
|
||||
func newApp(logger log.Logger, db dbm.DB) abci.Application {
|
||||
return app.NewDemocoinApp(logger, db)
|
||||
}
|
||||
|
||||
func exportAppState(logger log.Logger, db dbm.DB) (json.RawMessage, error) {
|
||||
dapp := app.NewDemocoinApp(logger, db)
|
||||
return dapp.ExportAppStateJSON()
|
||||
}
|
||||
|
||||
func main() {
|
||||
server.AddCommands(rootCmd, defaultAppState, generateApp, context)
|
||||
cdc := app.MakeCodec()
|
||||
ctx := server.NewDefaultContext()
|
||||
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "democoind",
|
||||
Short: "Democoin Daemon (server)",
|
||||
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
|
||||
}
|
||||
|
||||
server.AddCommands(ctx, cdc, rootCmd, CoolAppInit,
|
||||
server.ConstructAppCreator(newApp, "democoin"),
|
||||
server.ConstructAppExporter(exportAppState, "democoin"))
|
||||
|
||||
// prepare and add flags
|
||||
rootDir := os.ExpandEnv("$HOME/.democoind")
|
||||
|
|
|
@ -45,8 +45,8 @@ func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder {
|
|||
// State to Unmarshal
|
||||
type GenesisState struct {
|
||||
Accounts []*GenesisAccount `json:"accounts"`
|
||||
PowGenesis pow.PowGenesis `json:"pow"`
|
||||
CoolGenesis cool.CoolGenesis `json:"cool"`
|
||||
POWGenesis pow.Genesis `json:"pow"`
|
||||
CoolGenesis cool.Genesis `json:"cool"`
|
||||
}
|
||||
|
||||
// GenesisAccount doesn't need pubkey or sequence
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
package commands
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
||||
)
|
||||
|
@ -20,11 +19,8 @@ func QuizTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
return &cobra.Command{
|
||||
Use: "cool [answer]",
|
||||
Short: "What's cooler than being cool?",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
return errors.New("You must provide an answer")
|
||||
}
|
||||
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
||||
// get the from address from the name flag
|
||||
|
@ -34,19 +30,13 @@ func QuizTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
|
||||
// create the message
|
||||
msg := cool.NewQuizMsg(from, args[0])
|
||||
msg := cool.NewMsgQuiz(from, args[0])
|
||||
|
||||
// get account name
|
||||
name := viper.GetString(client.FlagName)
|
||||
|
||||
// default to next sequence number if none provided
|
||||
ctx, err = context.EnsureSequence(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
|
||||
res, err := ctx.EnsureSignBuildBroadcast(name, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -62,11 +52,8 @@ func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
return &cobra.Command{
|
||||
Use: "setcool [answer]",
|
||||
Short: "You're so cool, tell us what is cool!",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
return errors.New("You must provide an answer")
|
||||
}
|
||||
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
||||
// get the from address from the name flag
|
||||
|
@ -78,17 +65,11 @@ func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
// get account name
|
||||
name := viper.GetString(client.FlagName)
|
||||
|
||||
// default to next sequence number if none provided
|
||||
ctx, err = context.EnsureSequence(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the message
|
||||
msg := cool.NewSetTrendMsg(from, args[0])
|
||||
msg := cool.NewMsgSetTrend(from, args[0])
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
|
||||
res, err := ctx.EnsureSignBuildBroadcast(name, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
|
@ -6,12 +6,15 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Cool errors reserve 400 ~ 499.
|
||||
const (
|
||||
DefaultCodespace sdk.CodespaceType = 6
|
||||
|
||||
// Cool module reserves error 400-499 lawl
|
||||
CodeIncorrectCoolAnswer sdk.CodeType = 400
|
||||
)
|
||||
|
||||
// ErrIncorrectCoolAnswer - Error returned upon an incorrect guess
|
||||
func ErrIncorrectCoolAnswer(answer string) sdk.Error {
|
||||
return sdk.NewError(CodeIncorrectCoolAnswer, fmt.Sprintf("Incorrect cool answer: %v", answer))
|
||||
func ErrIncorrectCoolAnswer(codespace sdk.CodespaceType, answer string) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeIncorrectCoolAnswer, fmt.Sprintf("Incorrect cool answer: %v", answer))
|
||||
}
|
||||
|
|
|
@ -21,10 +21,10 @@ import (
|
|||
func NewHandler(k Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case SetTrendMsg:
|
||||
return handleSetTrendMsg(ctx, k, msg)
|
||||
case QuizMsg:
|
||||
return handleQuizMsg(ctx, k, msg)
|
||||
case MsgSetTrend:
|
||||
return handleMsgSetTrend(ctx, k, msg)
|
||||
case MsgQuiz:
|
||||
return handleMsgQuiz(ctx, k, msg)
|
||||
default:
|
||||
errMsg := fmt.Sprintf("Unrecognized cool Msg type: %v", reflect.TypeOf(msg).Name())
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
|
@ -32,19 +32,19 @@ func NewHandler(k Keeper) sdk.Handler {
|
|||
}
|
||||
}
|
||||
|
||||
// Handle QuizMsg This is the engine of your module
|
||||
func handleSetTrendMsg(ctx sdk.Context, k Keeper, msg SetTrendMsg) sdk.Result {
|
||||
// Handle MsgQuiz This is the engine of your module
|
||||
func handleMsgSetTrend(ctx sdk.Context, k Keeper, msg MsgSetTrend) sdk.Result {
|
||||
k.setTrend(ctx, msg.Cool)
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
// Handle QuizMsg This is the engine of your module
|
||||
func handleQuizMsg(ctx sdk.Context, k Keeper, msg QuizMsg) sdk.Result {
|
||||
// Handle MsgQuiz This is the engine of your module
|
||||
func handleMsgQuiz(ctx sdk.Context, k Keeper, msg MsgQuiz) sdk.Result {
|
||||
|
||||
correct := k.CheckTrend(ctx, msg.CoolAnswer)
|
||||
|
||||
if !correct {
|
||||
return ErrIncorrectCoolAnswer(msg.CoolAnswer).Result()
|
||||
return ErrIncorrectCoolAnswer(k.codespace, msg.CoolAnswer).Result()
|
||||
}
|
||||
|
||||
if ctx.IsCheckTx() {
|
||||
|
|
|
@ -7,14 +7,16 @@ import (
|
|||
|
||||
// Keeper - handlers sets/gets of custom variables for your module
|
||||
type Keeper struct {
|
||||
ck bank.CoinKeeper
|
||||
ck bank.Keeper
|
||||
|
||||
storeKey sdk.StoreKey // The (unexposed) key used to access the store from the Context.
|
||||
|
||||
codespace sdk.CodespaceType
|
||||
}
|
||||
|
||||
// NewKeeper - Returns the Keeper
|
||||
func NewKeeper(key sdk.StoreKey, bankKeeper bank.CoinKeeper) Keeper {
|
||||
return Keeper{bankKeeper, key}
|
||||
func NewKeeper(key sdk.StoreKey, bankKeeper bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
||||
return Keeper{bankKeeper, key, codespace}
|
||||
}
|
||||
|
||||
// Key to knowing the trend on the streets!
|
||||
|
@ -42,7 +44,13 @@ func (k Keeper) CheckTrend(ctx sdk.Context, guessedTrend string) bool {
|
|||
}
|
||||
|
||||
// InitGenesis - store the genesis trend
|
||||
func (k Keeper) InitGenesis(ctx sdk.Context, data CoolGenesis) error {
|
||||
func InitGenesis(ctx sdk.Context, k Keeper, data Genesis) error {
|
||||
k.setTrend(ctx, data.Trend)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteGenesis - output the genesis trend
|
||||
func WriteGenesis(ctx sdk.Context, k Keeper) Genesis {
|
||||
trend := k.GetTrend(ctx)
|
||||
return Genesis{trend}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -8,39 +8,38 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// A really cool msg type, these fields are can be entirely arbitrary and
|
||||
// a really cool msg type, these fields are can be entirely arbitrary and
|
||||
// custom to your message
|
||||
type SetTrendMsg struct {
|
||||
type MsgSetTrend struct {
|
||||
Sender sdk.Address
|
||||
Cool string
|
||||
}
|
||||
|
||||
// Genesis state - specify genesis trend
|
||||
type CoolGenesis struct {
|
||||
// genesis state - specify genesis trend
|
||||
type Genesis struct {
|
||||
Trend string `json:"trend"`
|
||||
}
|
||||
|
||||
// New cool message
|
||||
func NewSetTrendMsg(sender sdk.Address, cool string) SetTrendMsg {
|
||||
return SetTrendMsg{
|
||||
// new cool message
|
||||
func NewMsgSetTrend(sender sdk.Address, cool string) MsgSetTrend {
|
||||
return MsgSetTrend{
|
||||
Sender: sender,
|
||||
Cool: cool,
|
||||
}
|
||||
}
|
||||
|
||||
// enforce the msg type at compile time
|
||||
var _ sdk.Msg = SetTrendMsg{}
|
||||
var _ sdk.Msg = MsgSetTrend{}
|
||||
|
||||
// nolint
|
||||
func (msg SetTrendMsg) Type() string { return "cool" }
|
||||
func (msg SetTrendMsg) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg SetTrendMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg SetTrendMsg) String() string {
|
||||
return fmt.Sprintf("SetTrendMsg{Sender: %v, Cool: %v}", msg.Sender, msg.Cool)
|
||||
func (msg MsgSetTrend) Type() string { return "cool" }
|
||||
func (msg MsgSetTrend) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg MsgSetTrend) String() string {
|
||||
return fmt.Sprintf("MsgSetTrend{Sender: %v, Cool: %v}", msg.Sender, msg.Cool)
|
||||
}
|
||||
|
||||
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
|
||||
func (msg SetTrendMsg) ValidateBasic() sdk.Error {
|
||||
func (msg MsgSetTrend) ValidateBasic() sdk.Error {
|
||||
if len(msg.Sender) == 0 {
|
||||
return sdk.ErrUnknownAddress(msg.Sender.String()).Trace("")
|
||||
}
|
||||
|
@ -54,7 +53,7 @@ func (msg SetTrendMsg) ValidateBasic() sdk.Error {
|
|||
}
|
||||
|
||||
// Get the bytes for the message signer to sign on
|
||||
func (msg SetTrendMsg) GetSignBytes() []byte {
|
||||
func (msg MsgSetTrend) GetSignBytes() []byte {
|
||||
b, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -66,32 +65,31 @@ func (msg SetTrendMsg) GetSignBytes() []byte {
|
|||
|
||||
// A message type to quiz how cool you are. these fields are can be entirely
|
||||
// arbitrary and custom to your message
|
||||
type QuizMsg struct {
|
||||
type MsgQuiz struct {
|
||||
Sender sdk.Address
|
||||
CoolAnswer string
|
||||
}
|
||||
|
||||
// New cool message
|
||||
func NewQuizMsg(sender sdk.Address, coolerthancool string) QuizMsg {
|
||||
return QuizMsg{
|
||||
func NewMsgQuiz(sender sdk.Address, coolerthancool string) MsgQuiz {
|
||||
return MsgQuiz{
|
||||
Sender: sender,
|
||||
CoolAnswer: coolerthancool,
|
||||
}
|
||||
}
|
||||
|
||||
// enforce the msg type at compile time
|
||||
var _ sdk.Msg = QuizMsg{}
|
||||
var _ sdk.Msg = MsgQuiz{}
|
||||
|
||||
// nolint
|
||||
func (msg QuizMsg) Type() string { return "cool" }
|
||||
func (msg QuizMsg) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg QuizMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg QuizMsg) String() string {
|
||||
return fmt.Sprintf("QuizMsg{Sender: %v, CoolAnswer: %v}", msg.Sender, msg.CoolAnswer)
|
||||
func (msg MsgQuiz) Type() string { return "cool" }
|
||||
func (msg MsgQuiz) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg MsgQuiz) String() string {
|
||||
return fmt.Sprintf("MsgQuiz{Sender: %v, CoolAnswer: %v}", msg.Sender, msg.CoolAnswer)
|
||||
}
|
||||
|
||||
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
|
||||
func (msg QuizMsg) ValidateBasic() sdk.Error {
|
||||
func (msg MsgQuiz) ValidateBasic() sdk.Error {
|
||||
if len(msg.Sender) == 0 {
|
||||
return sdk.ErrUnknownAddress(msg.Sender.String()).Trace("")
|
||||
}
|
||||
|
@ -99,7 +97,7 @@ func (msg QuizMsg) ValidateBasic() sdk.Error {
|
|||
}
|
||||
|
||||
// Get the bytes for the message signer to sign on
|
||||
func (msg QuizMsg) GetSignBytes() []byte {
|
||||
func (msg MsgQuiz) GetSignBytes() []byte {
|
||||
b, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -1,30 +1,25 @@
|
|||
package commands
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
||||
)
|
||||
|
||||
// command to mine some pow!
|
||||
func MineCmd(cdc *wire.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "mine [difficulty] [count] [nonce] [solution]",
|
||||
Short: "Mine some coins with proof-of-work!",
|
||||
Args: cobra.ExactArgs(4),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 4 {
|
||||
return errors.New("You must provide a difficulty, a count, a solution, and a nonce (in that order)")
|
||||
}
|
||||
|
||||
// get from address and parse arguments
|
||||
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
||||
from, err := ctx.GetFromAddress()
|
||||
|
@ -36,12 +31,10 @@ func MineCmd(cdc *wire.Codec) *cobra.Command {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
count, err := strconv.ParseUint(args[1], 0, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nonce, err := strconv.ParseUint(args[2], 0, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -49,19 +42,13 @@ func MineCmd(cdc *wire.Codec) *cobra.Command {
|
|||
|
||||
solution := []byte(args[3])
|
||||
|
||||
msg := pow.NewMineMsg(from, difficulty, count, nonce, solution)
|
||||
msg := pow.NewMsgMine(from, difficulty, count, nonce, solution)
|
||||
|
||||
// get account name
|
||||
name := ctx.FromAddressName
|
||||
|
||||
// default to next sequence number if none provided
|
||||
ctx, err = context.EnsureSequence(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
|
||||
res, err := ctx.EnsureSignBuildBroadcast(name, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
|
@ -4,9 +4,12 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// TODO remove, seems hacky
|
||||
type CodeType = sdk.CodeType
|
||||
|
||||
// POW errors reserve 200 ~ 299
|
||||
const (
|
||||
DefaultCodespace sdk.CodespaceType = 5
|
||||
CodeInvalidDifficulty CodeType = 201
|
||||
CodeNonexistentDifficulty CodeType = 202
|
||||
CodeNonexistentReward CodeType = 203
|
||||
|
@ -40,43 +43,37 @@ func codeToDefaultMsg(code CodeType) string {
|
|||
}
|
||||
}
|
||||
|
||||
func ErrInvalidDifficulty(msg string) sdk.Error {
|
||||
return newError(CodeInvalidDifficulty, msg)
|
||||
// nolint
|
||||
func ErrInvalidDifficulty(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||
return newError(codespace, CodeInvalidDifficulty, msg)
|
||||
}
|
||||
|
||||
func ErrNonexistentDifficulty() sdk.Error {
|
||||
return newError(CodeNonexistentDifficulty, "")
|
||||
func ErrNonexistentDifficulty(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeNonexistentDifficulty, "")
|
||||
}
|
||||
|
||||
func ErrNonexistentReward() sdk.Error {
|
||||
return newError(CodeNonexistentReward, "")
|
||||
func ErrNonexistentReward(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeNonexistentReward, "")
|
||||
}
|
||||
|
||||
func ErrNonexistentCount() sdk.Error {
|
||||
return newError(CodeNonexistentCount, "")
|
||||
func ErrNonexistentCount(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeNonexistentCount, "")
|
||||
}
|
||||
|
||||
func ErrInvalidProof(msg string) sdk.Error {
|
||||
return newError(CodeInvalidProof, msg)
|
||||
func ErrInvalidProof(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||
return newError(codespace, CodeInvalidProof, msg)
|
||||
}
|
||||
|
||||
func ErrNotBelowTarget(msg string) sdk.Error {
|
||||
return newError(CodeNotBelowTarget, msg)
|
||||
func ErrNotBelowTarget(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||
return newError(codespace, CodeNotBelowTarget, msg)
|
||||
}
|
||||
|
||||
func ErrInvalidCount(msg string) sdk.Error {
|
||||
return newError(CodeInvalidCount, msg)
|
||||
func ErrInvalidCount(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||
return newError(codespace, CodeInvalidCount, msg)
|
||||
}
|
||||
|
||||
func msgOrDefaultMsg(msg string, code CodeType) string {
|
||||
if msg != "" {
|
||||
return msg
|
||||
} else {
|
||||
return codeToDefaultMsg(code)
|
||||
}
|
||||
return codeToDefaultMsg(code)
|
||||
}
|
||||
|
||||
func newError(code CodeType, msg string) sdk.Error {
|
||||
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
|
||||
msg = msgOrDefaultMsg(msg, code)
|
||||
return sdk.NewError(code, msg)
|
||||
return sdk.NewError(codespace, code, msg)
|
||||
}
|
||||
|
|
|
@ -6,17 +6,18 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// POW handler
|
||||
func (pk Keeper) Handler(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case MineMsg:
|
||||
return handleMineMsg(ctx, pk, msg)
|
||||
case MsgMine:
|
||||
return handleMsgMine(ctx, pk, msg)
|
||||
default:
|
||||
errMsg := "Unrecognized pow Msg type: " + reflect.TypeOf(msg).Name()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
}
|
||||
}
|
||||
|
||||
func handleMineMsg(ctx sdk.Context, pk Keeper, msg MineMsg) sdk.Result {
|
||||
func handleMsgMine(ctx sdk.Context, pk Keeper, msg MsgMine) sdk.Result {
|
||||
|
||||
// precondition: msg has passed ValidateBasic
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||
|
@ -19,10 +20,10 @@ func TestPowHandler(t *testing.T) {
|
|||
auth.RegisterBaseAccount(cdc)
|
||||
|
||||
am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{})
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
||||
config := NewPowConfig("pow", int64(1))
|
||||
ck := bank.NewCoinKeeper(am)
|
||||
keeper := NewKeeper(capKey, config, ck)
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
|
||||
config := NewConfig("pow", int64(1))
|
||||
ck := bank.NewKeeper(am)
|
||||
keeper := NewKeeper(capKey, config, ck, DefaultCodespace)
|
||||
|
||||
handler := keeper.Handler
|
||||
|
||||
|
@ -30,11 +31,11 @@ func TestPowHandler(t *testing.T) {
|
|||
count := uint64(1)
|
||||
difficulty := uint64(2)
|
||||
|
||||
err := keeper.InitGenesis(ctx, PowGenesis{uint64(1), uint64(0)})
|
||||
err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)})
|
||||
assert.Nil(t, err)
|
||||
|
||||
nonce, proof := mine(addr, count, difficulty)
|
||||
msg := NewMineMsg(addr, difficulty, count, nonce, proof)
|
||||
msg := NewMsgMine(addr, difficulty, count, nonce, proof)
|
||||
|
||||
result := handler(ctx, msg)
|
||||
assert.Equal(t, result, sdk.Result{})
|
||||
|
@ -51,7 +52,7 @@ func TestPowHandler(t *testing.T) {
|
|||
|
||||
difficulty = uint64(4)
|
||||
nonce, proof = mine(addr, count, difficulty)
|
||||
msg = NewMineMsg(addr, difficulty, count, nonce, proof)
|
||||
msg = NewMsgMine(addr, difficulty, count, nonce, proof)
|
||||
|
||||
result = handler(ctx, msg)
|
||||
assert.NotEqual(t, result, sdk.Result{})
|
||||
|
|
|
@ -9,41 +9,61 @@ import (
|
|||
)
|
||||
|
||||
// module users must specify coin denomination and reward (constant) per PoW solution
|
||||
type PowConfig struct {
|
||||
type Config struct {
|
||||
Denomination string
|
||||
Reward int64
|
||||
}
|
||||
|
||||
// genesis info must specify starting difficulty and starting count
|
||||
type PowGenesis struct {
|
||||
type Genesis struct {
|
||||
Difficulty uint64 `json:"difficulty"`
|
||||
Count uint64 `json:"count"`
|
||||
}
|
||||
|
||||
// POW Keeper
|
||||
type Keeper struct {
|
||||
key sdk.StoreKey
|
||||
config PowConfig
|
||||
ck bank.CoinKeeper
|
||||
config Config
|
||||
ck bank.Keeper
|
||||
codespace sdk.CodespaceType
|
||||
}
|
||||
|
||||
func NewPowConfig(denomination string, reward int64) PowConfig {
|
||||
return PowConfig{denomination, reward}
|
||||
func NewConfig(denomination string, reward int64) Config {
|
||||
return Config{denomination, reward}
|
||||
}
|
||||
|
||||
func NewKeeper(key sdk.StoreKey, config PowConfig, ck bank.CoinKeeper) Keeper {
|
||||
return Keeper{key, config, ck}
|
||||
func NewKeeper(key sdk.StoreKey, config Config, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
||||
return Keeper{key, config, ck, codespace}
|
||||
}
|
||||
|
||||
func (pk Keeper) InitGenesis(ctx sdk.Context, genesis PowGenesis) error {
|
||||
pk.SetLastDifficulty(ctx, genesis.Difficulty)
|
||||
pk.SetLastCount(ctx, genesis.Count)
|
||||
// InitGenesis for the POW module
|
||||
func InitGenesis(ctx sdk.Context, k Keeper, genesis Genesis) error {
|
||||
k.SetLastDifficulty(ctx, genesis.Difficulty)
|
||||
k.SetLastCount(ctx, genesis.Count)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteGenesis for the PoW module
|
||||
func WriteGenesis(ctx sdk.Context, k Keeper) Genesis {
|
||||
difficulty, err := k.GetLastDifficulty(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
count, err := k.GetLastCount(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return Genesis{
|
||||
difficulty,
|
||||
count,
|
||||
}
|
||||
}
|
||||
|
||||
var lastDifficultyKey = []byte("lastDifficultyKey")
|
||||
|
||||
func (pk Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
// get the last mining difficulty
|
||||
func (k Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(k.key)
|
||||
stored := store.Get(lastDifficultyKey)
|
||||
if stored == nil {
|
||||
panic("no stored difficulty")
|
||||
|
@ -52,15 +72,17 @@ func (pk Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (pk Keeper) SetLastDifficulty(ctx sdk.Context, diff uint64) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
// set the last mining difficulty
|
||||
func (k Keeper) SetLastDifficulty(ctx sdk.Context, diff uint64) {
|
||||
store := ctx.KVStore(k.key)
|
||||
store.Set(lastDifficultyKey, []byte(strconv.FormatUint(diff, 16)))
|
||||
}
|
||||
|
||||
var countKey = []byte("count")
|
||||
|
||||
func (pk Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
// get the last count
|
||||
func (k Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(k.key)
|
||||
stored := store.Get(countKey)
|
||||
if stored == nil {
|
||||
panic("no stored count")
|
||||
|
@ -69,45 +91,45 @@ func (pk Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (pk Keeper) SetLastCount(ctx sdk.Context, count uint64) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
// set the last count
|
||||
func (k Keeper) SetLastCount(ctx sdk.Context, count uint64) {
|
||||
store := ctx.KVStore(k.key)
|
||||
store.Set(countKey, []byte(strconv.FormatUint(count, 16)))
|
||||
}
|
||||
|
||||
func (pk Keeper) CheckValid(ctx sdk.Context, difficulty uint64, count uint64) (uint64, uint64, sdk.Error) {
|
||||
// Is the keeper state valid?
|
||||
func (k Keeper) CheckValid(ctx sdk.Context, difficulty uint64, count uint64) (uint64, uint64, sdk.Error) {
|
||||
|
||||
lastDifficulty, err := pk.GetLastDifficulty(ctx)
|
||||
lastDifficulty, err := k.GetLastDifficulty(ctx)
|
||||
if err != nil {
|
||||
return 0, 0, ErrNonexistentDifficulty()
|
||||
return 0, 0, ErrNonexistentDifficulty(k.codespace)
|
||||
}
|
||||
|
||||
newDifficulty := lastDifficulty + 1
|
||||
|
||||
lastCount, err := pk.GetLastCount(ctx)
|
||||
lastCount, err := k.GetLastCount(ctx)
|
||||
if err != nil {
|
||||
return 0, 0, ErrNonexistentCount()
|
||||
return 0, 0, ErrNonexistentCount(k.codespace)
|
||||
}
|
||||
|
||||
newCount := lastCount + 1
|
||||
|
||||
if count != newCount {
|
||||
return 0, 0, ErrInvalidCount(fmt.Sprintf("invalid count: was %d, should have been %d", count, newCount))
|
||||
return 0, 0, ErrInvalidCount(k.codespace, fmt.Sprintf("invalid count: was %d, should have been %d", count, newCount))
|
||||
}
|
||||
|
||||
if difficulty != newDifficulty {
|
||||
return 0, 0, ErrInvalidDifficulty(fmt.Sprintf("invalid difficulty: was %d, should have been %d", difficulty, newDifficulty))
|
||||
return 0, 0, ErrInvalidDifficulty(k.codespace, fmt.Sprintf("invalid difficulty: was %d, should have been %d", difficulty, newDifficulty))
|
||||
}
|
||||
|
||||
return newDifficulty, newCount, nil
|
||||
|
||||
}
|
||||
|
||||
func (pk Keeper) ApplyValid(ctx sdk.Context, sender sdk.Address, newDifficulty uint64, newCount uint64) sdk.Error {
|
||||
_, ckErr := pk.ck.AddCoins(ctx, sender, []sdk.Coin{sdk.Coin{pk.config.Denomination, pk.config.Reward}})
|
||||
// Add some coins for a POW well done
|
||||
func (k Keeper) ApplyValid(ctx sdk.Context, sender sdk.Address, newDifficulty uint64, newCount uint64) sdk.Error {
|
||||
_, ckErr := k.ck.AddCoins(ctx, sender, []sdk.Coin{sdk.Coin{k.config.Denomination, k.config.Reward}})
|
||||
if ckErr != nil {
|
||||
return ckErr
|
||||
}
|
||||
pk.SetLastDifficulty(ctx, newDifficulty)
|
||||
pk.SetLastCount(ctx, newCount)
|
||||
k.SetLastDifficulty(ctx, newDifficulty)
|
||||
k.SetLastCount(ctx, newCount)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -32,14 +33,18 @@ func TestPowKeeperGetSet(t *testing.T) {
|
|||
auth.RegisterBaseAccount(cdc)
|
||||
|
||||
am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{})
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
||||
config := NewPowConfig("pow", int64(1))
|
||||
ck := bank.NewCoinKeeper(am)
|
||||
keeper := NewKeeper(capKey, config, ck)
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
|
||||
config := NewConfig("pow", int64(1))
|
||||
ck := bank.NewKeeper(am)
|
||||
keeper := NewKeeper(capKey, config, ck, DefaultCodespace)
|
||||
|
||||
err := keeper.InitGenesis(ctx, PowGenesis{uint64(1), uint64(0)})
|
||||
err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)})
|
||||
assert.Nil(t, err)
|
||||
|
||||
genesis := WriteGenesis(ctx, keeper)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, genesis, Genesis{uint64(1), uint64(0)})
|
||||
|
||||
res, err := keeper.GetLastDifficulty(ctx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, res, uint64(1))
|
||||
|
|
|
@ -9,9 +9,10 @@ import (
|
|||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
func GenerateMineMsg(sender sdk.Address, count uint64, difficulty uint64) MineMsg {
|
||||
// generate the mine message
|
||||
func GenerateMsgMine(sender sdk.Address, count uint64, difficulty uint64) MsgMine {
|
||||
nonce, hash := mine(sender, count, difficulty)
|
||||
return NewMineMsg(sender, difficulty, count, nonce, hash)
|
||||
return NewMsgMine(sender, difficulty, count, nonce, hash)
|
||||
}
|
||||
|
||||
func hash(sender sdk.Address, count uint64, nonce uint64) []byte {
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// MineMsg - mine some coins with PoW
|
||||
type MineMsg struct {
|
||||
// MsgMine - mine some coins with PoW
|
||||
type MsgMine struct {
|
||||
Sender sdk.Address `json:"sender"`
|
||||
Difficulty uint64 `json:"difficulty"`
|
||||
Count uint64 `json:"count"`
|
||||
|
@ -23,21 +23,22 @@ type MineMsg struct {
|
|||
}
|
||||
|
||||
// enforce the msg type at compile time
|
||||
var _ sdk.Msg = MineMsg{}
|
||||
var _ sdk.Msg = MsgMine{}
|
||||
|
||||
// NewMineMsg - construct mine message
|
||||
func NewMineMsg(sender sdk.Address, difficulty uint64, count uint64, nonce uint64, proof []byte) MineMsg {
|
||||
return MineMsg{sender, difficulty, count, nonce, proof}
|
||||
// NewMsgMine - construct mine message
|
||||
func NewMsgMine(sender sdk.Address, difficulty uint64, count uint64, nonce uint64, proof []byte) MsgMine {
|
||||
return MsgMine{sender, difficulty, count, nonce, proof}
|
||||
}
|
||||
|
||||
func (msg MineMsg) Type() string { return "pow" }
|
||||
func (msg MineMsg) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg MineMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg MineMsg) String() string {
|
||||
return fmt.Sprintf("MineMsg{Sender: %v, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
|
||||
// nolint
|
||||
func (msg MsgMine) Type() string { return "pow" }
|
||||
func (msg MsgMine) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg MsgMine) String() string {
|
||||
return fmt.Sprintf("MsgMine{Sender: %v, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
|
||||
}
|
||||
|
||||
func (msg MineMsg) ValidateBasic() sdk.Error {
|
||||
// validate the mine message
|
||||
func (msg MsgMine) ValidateBasic() sdk.Error {
|
||||
// check hash
|
||||
var data []byte
|
||||
// hash must include sender, so no other users can race the tx
|
||||
|
@ -52,7 +53,7 @@ func (msg MineMsg) ValidateBasic() sdk.Error {
|
|||
hex.Encode(hashHex, hash)
|
||||
hashHex = hashHex[:16]
|
||||
if !bytes.Equal(hashHex, msg.Proof) {
|
||||
return ErrInvalidProof(fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
|
||||
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
|
||||
}
|
||||
|
||||
// check proof below difficulty
|
||||
|
@ -60,16 +61,17 @@ func (msg MineMsg) ValidateBasic() sdk.Error {
|
|||
target := math.MaxUint64 / msg.Difficulty
|
||||
hashUint, err := strconv.ParseUint(string(msg.Proof), 16, 64)
|
||||
if err != nil {
|
||||
return ErrInvalidProof(fmt.Sprintf("proof: %s", msg.Proof))
|
||||
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("proof: %s", msg.Proof))
|
||||
}
|
||||
if hashUint >= target {
|
||||
return ErrNotBelowTarget(fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
|
||||
return ErrNotBelowTarget(DefaultCodespace, fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg MineMsg) GetSignBytes() []byte {
|
||||
// get the mine message sign bytes
|
||||
func (msg MsgMine) GetSignBytes() []byte {
|
||||
b, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
|
@ -9,70 +9,65 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestNewMineMsg(t *testing.T) {
|
||||
func TestNewMsgMine(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 0, 0, 0, []byte("")}
|
||||
equiv := NewMineMsg(addr, 0, 0, 0, []byte(""))
|
||||
msg := MsgMine{addr, 0, 0, 0, []byte("")}
|
||||
equiv := NewMsgMine(addr, 0, 0, 0, []byte(""))
|
||||
assert.Equal(t, msg, equiv, "%s != %s", msg, equiv)
|
||||
}
|
||||
|
||||
func TestMineMsgType(t *testing.T) {
|
||||
func TestMsgMineType(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 0, 0, 0, []byte("")}
|
||||
msg := MsgMine{addr, 0, 0, 0, []byte("")}
|
||||
assert.Equal(t, msg.Type(), "pow")
|
||||
}
|
||||
|
||||
func TestMineMsgValidation(t *testing.T) {
|
||||
func TestMsgMineValidation(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
otherAddr := sdk.Address([]byte("another"))
|
||||
count := uint64(0)
|
||||
|
||||
for difficulty := uint64(1); difficulty < 1000; difficulty += 100 {
|
||||
count += 1
|
||||
|
||||
count++
|
||||
nonce, proof := mine(addr, count, difficulty)
|
||||
msg := MineMsg{addr, difficulty, count, nonce, proof}
|
||||
msg := MsgMine{addr, difficulty, count, nonce, proof}
|
||||
err := msg.ValidateBasic()
|
||||
assert.Nil(t, err, "error with difficulty %d - %+v", difficulty, err)
|
||||
|
||||
msg.Count += 1
|
||||
msg.Count++
|
||||
err = msg.ValidateBasic()
|
||||
assert.NotNil(t, err, "count was wrong, should have thrown error with msg %s", msg)
|
||||
|
||||
msg.Count -= 1
|
||||
msg.Nonce += 1
|
||||
msg.Count--
|
||||
msg.Nonce++
|
||||
err = msg.ValidateBasic()
|
||||
assert.NotNil(t, err, "nonce was wrong, should have thrown error with msg %s", msg)
|
||||
|
||||
msg.Nonce -= 1
|
||||
msg.Nonce--
|
||||
msg.Sender = otherAddr
|
||||
err = msg.ValidateBasic()
|
||||
assert.NotNil(t, err, "sender was wrong, should have thrown error with msg %s", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMineMsgString(t *testing.T) {
|
||||
func TestMsgMineString(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 0, 0, 0, []byte("abc")}
|
||||
msg := MsgMine{addr, 0, 0, 0, []byte("abc")}
|
||||
res := msg.String()
|
||||
assert.Equal(t, res, "MineMsg{Sender: 73656E646572, Difficulty: 0, Count: 0, Nonce: 0, Proof: abc}")
|
||||
assert.Equal(t, res, "MsgMine{Sender: 73656E646572, Difficulty: 0, Count: 0, Nonce: 0, Proof: abc}")
|
||||
}
|
||||
|
||||
func TestMineMsgGet(t *testing.T) {
|
||||
func TestMsgMineGetSignBytes(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 0, 0, 0, []byte("")}
|
||||
res := msg.Get(nil)
|
||||
assert.Nil(t, res)
|
||||
}
|
||||
|
||||
func TestMineMsgGetSignBytes(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 1, 1, 1, []byte("abc")}
|
||||
msg := MsgMine{addr, 1, 1, 1, []byte("abc")}
|
||||
res := msg.GetSignBytes()
|
||||
assert.Equal(t, string(res), `{"sender":"73656E646572","difficulty":1,"count":1,"nonce":1,"proof":"YWJj"}`)
|
||||
}
|
||||
|
||||
func TestMineMsgGetSigners(t *testing.T) {
|
||||
func TestMsgMineGetSigners(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 1, 1, 1, []byte("abc")}
|
||||
msg := MsgMine{addr, 1, 1, 1, []byte("abc")}
|
||||
res := msg.GetSigners()
|
||||
assert.Equal(t, fmt.Sprintf("%v", res), "[73656E646572]")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -10,17 +10,17 @@ import (
|
|||
func NewHandler(k Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case BondMsg:
|
||||
return handleBondMsg(ctx, k, msg)
|
||||
case UnbondMsg:
|
||||
return handleUnbondMsg(ctx, k, msg)
|
||||
case MsgBond:
|
||||
return handleMsgBond(ctx, k, msg)
|
||||
case MsgUnbond:
|
||||
return handleMsgUnbond(ctx, k, msg)
|
||||
default:
|
||||
return sdk.ErrUnknownRequest("No match for message type.").Result()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleBondMsg(ctx sdk.Context, k Keeper, msg BondMsg) sdk.Result {
|
||||
func handleMsgBond(ctx sdk.Context, k Keeper, msg MsgBond) sdk.Result {
|
||||
power, err := k.Bond(ctx, msg.Address, msg.PubKey, msg.Stake)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
|
@ -32,12 +32,12 @@ func handleBondMsg(ctx sdk.Context, k Keeper, msg BondMsg) sdk.Result {
|
|||
}
|
||||
|
||||
return sdk.Result{
|
||||
Code: sdk.CodeOK,
|
||||
Code: sdk.ABCICodeOK,
|
||||
ValidatorUpdates: abci.Validators{valSet},
|
||||
}
|
||||
}
|
||||
|
||||
func handleUnbondMsg(ctx sdk.Context, k Keeper, msg UnbondMsg) sdk.Result {
|
||||
func handleMsgUnbond(ctx sdk.Context, k Keeper, msg MsgUnbond) sdk.Result {
|
||||
pubKey, _, err := k.Unbond(ctx, msg.Address)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
|
@ -49,7 +49,7 @@ func handleUnbondMsg(ctx sdk.Context, k Keeper, msg UnbondMsg) sdk.Result {
|
|||
}
|
||||
|
||||
return sdk.Result{
|
||||
Code: sdk.CodeOK,
|
||||
Code: sdk.ABCICodeOK,
|
||||
ValidatorUpdates: abci.Validators{valSet},
|
||||
}
|
||||
}
|
|
@ -12,20 +12,23 @@ const stakingToken = "steak"
|
|||
|
||||
const moduleName = "simplestake"
|
||||
|
||||
// simple stake keeper
|
||||
type Keeper struct {
|
||||
ck bank.CoinKeeper
|
||||
ck bank.Keeper
|
||||
|
||||
key sdk.StoreKey
|
||||
cdc *wire.Codec
|
||||
codespace sdk.CodespaceType
|
||||
}
|
||||
|
||||
func NewKeeper(key sdk.StoreKey, coinKeeper bank.CoinKeeper) Keeper {
|
||||
func NewKeeper(key sdk.StoreKey, coinKeeper bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
||||
cdc := wire.NewCodec()
|
||||
wire.RegisterCrypto(cdc)
|
||||
return Keeper{
|
||||
key: key,
|
||||
cdc: cdc,
|
||||
ck: coinKeeper,
|
||||
codespace: codespace,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,9 +60,10 @@ func (k Keeper) deleteBondInfo(ctx sdk.Context, addr sdk.Address) {
|
|||
store.Delete(addr)
|
||||
}
|
||||
|
||||
// register a bond with the keeper
|
||||
func (k Keeper) Bond(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, stake sdk.Coin) (int64, sdk.Error) {
|
||||
if stake.Denom != stakingToken {
|
||||
return 0, ErrIncorrectStakingToken()
|
||||
return 0, ErrIncorrectStakingToken(k.codespace)
|
||||
}
|
||||
|
||||
_, err := k.ck.SubtractCoins(ctx, addr, []sdk.Coin{stake})
|
||||
|
@ -81,10 +85,11 @@ func (k Keeper) Bond(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, st
|
|||
return bi.Power, nil
|
||||
}
|
||||
|
||||
// register an unbond with the keeper
|
||||
func (k Keeper) Unbond(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64, sdk.Error) {
|
||||
bi := k.getBondInfo(ctx, addr)
|
||||
if bi.isEmpty() {
|
||||
return nil, 0, ErrInvalidUnbond()
|
||||
return nil, 0, ErrInvalidUnbond(k.codespace)
|
||||
}
|
||||
k.deleteBondInfo(ctx, addr)
|
||||
|
||||
|
@ -102,7 +107,7 @@ func (k Keeper) Unbond(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64,
|
|||
|
||||
func (k Keeper) bondWithoutCoins(ctx sdk.Context, addr sdk.Address, pubKey crypto.PubKey, stake sdk.Coin) (int64, sdk.Error) {
|
||||
if stake.Denom != stakingToken {
|
||||
return 0, ErrIncorrectStakingToken()
|
||||
return 0, ErrIncorrectStakingToken(k.codespace)
|
||||
}
|
||||
|
||||
bi := k.getBondInfo(ctx, addr)
|
||||
|
@ -122,7 +127,7 @@ func (k Keeper) bondWithoutCoins(ctx sdk.Context, addr sdk.Address, pubKey crypt
|
|||
func (k Keeper) unbondWithoutCoins(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, int64, sdk.Error) {
|
||||
bi := k.getBondInfo(ctx, addr)
|
||||
if bi.isEmpty() {
|
||||
return nil, 0, ErrInvalidUnbond()
|
||||
return nil, 0, ErrInvalidUnbond(k.codespace)
|
||||
}
|
||||
k.deleteBondInfo(ctx, addr)
|
||||
|
|
@ -10,6 +10,7 @@ import (
|
|||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -32,8 +33,8 @@ func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey, *sdk.KVStoreKey) {
|
|||
func TestKeeperGetSet(t *testing.T) {
|
||||
ms, _, capKey := setupMultiStore()
|
||||
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
||||
stakeKeeper := NewKeeper(capKey, bank.NewCoinKeeper(nil))
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
|
||||
stakeKeeper := NewKeeper(capKey, bank.NewKeeper(nil), DefaultCodespace)
|
||||
addr := sdk.Address([]byte("some-address"))
|
||||
|
||||
bi := stakeKeeper.getBondInfo(ctx, addr)
|
||||
|
@ -59,17 +60,17 @@ func TestBonding(t *testing.T) {
|
|||
cdc := wire.NewCodec()
|
||||
auth.RegisterBaseAccount(cdc)
|
||||
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger())
|
||||
|
||||
accountMapper := auth.NewAccountMapper(cdc, authKey, &auth.BaseAccount{})
|
||||
coinKeeper := bank.NewCoinKeeper(accountMapper)
|
||||
stakeKeeper := NewKeeper(capKey, coinKeeper)
|
||||
coinKeeper := bank.NewKeeper(accountMapper)
|
||||
stakeKeeper := NewKeeper(capKey, coinKeeper, DefaultCodespace)
|
||||
addr := sdk.Address([]byte("some-address"))
|
||||
privKey := crypto.GenPrivKeyEd25519()
|
||||
pubKey := privKey.PubKey()
|
||||
|
||||
_, _, err := stakeKeeper.unbondWithoutCoins(ctx, addr)
|
||||
assert.Equal(t, err, ErrInvalidUnbond())
|
||||
assert.Equal(t, err, ErrInvalidUnbond(DefaultCodespace))
|
||||
|
||||
_, err = stakeKeeper.bondWithoutCoins(ctx, addr, pubKey, sdk.Coin{"steak", 10})
|
||||
assert.Nil(t, err)
|
||||
|
@ -82,5 +83,5 @@ func TestBonding(t *testing.T) {
|
|||
assert.Equal(t, pubKey, pk)
|
||||
|
||||
_, _, err = stakeKeeper.unbondWithoutCoins(ctx, addr)
|
||||
assert.Equal(t, err, ErrInvalidUnbond())
|
||||
assert.Equal(t, err, ErrInvalidUnbond(DefaultCodespace))
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -14,14 +14,14 @@ func TestBondMsgValidation(t *testing.T) {
|
|||
privKey := crypto.GenPrivKeyEd25519()
|
||||
cases := []struct {
|
||||
valid bool
|
||||
bondMsg BondMsg
|
||||
msgBond MsgBond
|
||||
}{
|
||||
{true, NewBondMsg(sdk.Address{}, sdk.Coin{"mycoin", 5}, privKey.PubKey())},
|
||||
{false, NewBondMsg(sdk.Address{}, sdk.Coin{"mycoin", 0}, privKey.PubKey())},
|
||||
{true, NewMsgBond(sdk.Address{}, sdk.Coin{"mycoin", 5}, privKey.PubKey())},
|
||||
{false, NewMsgBond(sdk.Address{}, sdk.Coin{"mycoin", 0}, privKey.PubKey())},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
err := tc.bondMsg.ValidateBasic()
|
||||
err := tc.msgBond.ValidateBasic()
|
||||
if tc.valid {
|
||||
assert.Nil(t, err, "%d: %+v", i, err)
|
||||
} else {
|
|
@ -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)
|
||||
}
|
|
@ -32,7 +32,7 @@ func main() {
|
|||
var capKeyMainStore = sdk.NewKVStoreKey("main")
|
||||
|
||||
// Create BaseApp.
|
||||
var baseApp = bam.NewBaseApp("kvstore", logger, db)
|
||||
var baseApp = bam.NewBaseApp("kvstore", nil, logger, db)
|
||||
|
||||
// Set mounts for BaseApp's MultiStore.
|
||||
baseApp.MountStoresIAVL(capKeyMainStore)
|
||||
|
@ -41,7 +41,7 @@ func main() {
|
|||
baseApp.SetTxDecoder(decodeTx)
|
||||
|
||||
// Set a handler Route.
|
||||
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore))
|
||||
baseApp.Router().AddRoute("kvstore", Handler(capKeyMainStore))
|
||||
|
||||
// Load latest version.
|
||||
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
|
||||
|
@ -65,11 +65,12 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
func KVStoreHandler(storeKey sdk.StoreKey) sdk.Handler {
|
||||
// KVStore Handler
|
||||
func Handler(storeKey sdk.StoreKey) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
dTx, ok := msg.(kvstoreTx)
|
||||
if !ok {
|
||||
panic("KVStoreHandler should only receive kvstoreTx")
|
||||
panic("Handler should only receive kvstoreTx")
|
||||
}
|
||||
|
||||
// tx is already unmarshalled
|
||||
|
|
|
@ -13,19 +13,6 @@ type kvstoreTx struct {
|
|||
bytes []byte
|
||||
}
|
||||
|
||||
func (tx kvstoreTx) Get(key interface{}) (value interface{}) {
|
||||
switch k := key.(type) {
|
||||
case string:
|
||||
switch k {
|
||||
case "key":
|
||||
return tx.key
|
||||
case "value":
|
||||
return tx.value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx kvstoreTx) Type() string {
|
||||
return "kvstore"
|
||||
}
|
||||
|
|
32
mock/app.go
32
mock/app.go
|
@ -6,16 +6,18 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
// NewApp creates a simple mock kvstore app for testing.
|
||||
// It should work similar to a real app.
|
||||
// Make sure rootDir is empty before running the test,
|
||||
// NewApp creates a simple mock kvstore app for testing. It should work
|
||||
// similar to a real app. Make sure rootDir is empty before running the test,
|
||||
// in order to guarantee consistent results
|
||||
func NewApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||
db, err := dbm.NewGoLevelDB("mock", filepath.Join(rootDir, "data"))
|
||||
|
@ -27,7 +29,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
|||
capKeyMainStore := sdk.NewKVStoreKey("main")
|
||||
|
||||
// Create BaseApp.
|
||||
baseApp := bam.NewBaseApp("kvstore", logger, db)
|
||||
baseApp := bam.NewBaseApp("kvstore", nil, logger, db)
|
||||
|
||||
// Set mounts for BaseApp's MultiStore.
|
||||
baseApp.MountStoresIAVL(capKeyMainStore)
|
||||
|
@ -103,11 +105,10 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci
|
|||
}
|
||||
}
|
||||
|
||||
// GenInitOptions can be passed into InitCmd,
|
||||
// returns a static string of a few key-values that can be parsed
|
||||
// by InitChainer
|
||||
func GenInitOptions(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
|
||||
opts := []byte(`{
|
||||
// AppGenState can be passed into InitCmd, returns a static string of a few
|
||||
// key-values that can be parsed by InitChainer
|
||||
func AppGenState(_ *wire.Codec, _ []json.RawMessage) (appState json.RawMessage, err error) {
|
||||
appState = json.RawMessage(`{
|
||||
"values": [
|
||||
{
|
||||
"key": "hello",
|
||||
|
@ -119,5 +120,16 @@ func GenInitOptions(args []string, addr sdk.Address, coinDenom string) (json.Raw
|
|||
}
|
||||
]
|
||||
}`)
|
||||
return opts, nil
|
||||
return
|
||||
}
|
||||
|
||||
// Return a validator, not much else
|
||||
func AppGenTx(_ *wire.Codec, pk crypto.PubKey) (
|
||||
appGenTx, cliPrint json.RawMessage, validator tmtypes.GenesisValidator, err error) {
|
||||
|
||||
validator = tmtypes.GenesisValidator{
|
||||
PubKey: pk,
|
||||
Power: 10,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -21,9 +21,13 @@ func TestInitApp(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// initialize it future-way
|
||||
opts, err := GenInitOptions(nil, nil, "")
|
||||
appState, err := AppGenState(nil, nil)
|
||||
require.NoError(t, err)
|
||||
req := abci.RequestInitChain{AppStateBytes: opts}
|
||||
|
||||
//TODO test validators in the init chain?
|
||||
req := abci.RequestInitChain{
|
||||
AppStateBytes: appState,
|
||||
}
|
||||
app.InitChain(req)
|
||||
app.Commit()
|
||||
|
||||
|
|
|
@ -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)}
|
||||
}
|
|
@ -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))
|
||||
}
|
13
mock/tx.go
13
mock/tx.go
|
@ -26,19 +26,6 @@ func NewTx(key, value string) kvstoreTx {
|
|||
}
|
||||
}
|
||||
|
||||
func (tx kvstoreTx) Get(key interface{}) (value interface{}) {
|
||||
switch k := key.(type) {
|
||||
case string:
|
||||
switch k {
|
||||
case "key":
|
||||
return tx.key
|
||||
case "value":
|
||||
return tx.value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx kvstoreTx) Type() string {
|
||||
return "kvstore"
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue