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