merge from develop
This commit is contained in:
commit
8ff316c99e
|
@ -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,22 @@ 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_cover:
|
test_cover:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
parallelism: 4
|
parallelism: 4
|
||||||
|
@ -59,7 +81,9 @@ jobs:
|
||||||
- run:
|
- run:
|
||||||
name: Run tests
|
name: Run tests
|
||||||
command: |
|
command: |
|
||||||
for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v /vendor/ | circleci tests split --split-by=timings); do
|
export PATH="$GOBIN:$PATH"
|
||||||
|
make install
|
||||||
|
for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v /vendor/ | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test | circleci tests split --split-by=timings); do
|
||||||
id=$(basename "$pkg")
|
id=$(basename "$pkg")
|
||||||
|
|
||||||
go test -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg"
|
go test -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg"
|
||||||
|
@ -94,10 +118,12 @@ workflows:
|
||||||
test-suite:
|
test-suite:
|
||||||
jobs:
|
jobs:
|
||||||
- setup_dependencies
|
- setup_dependencies
|
||||||
|
- lint:
|
||||||
|
requires:
|
||||||
|
- setup_dependencies
|
||||||
- test_cover:
|
- test_cover:
|
||||||
requires:
|
requires:
|
||||||
- setup_dependencies
|
- setup_dependencies
|
||||||
- upload_coverage:
|
- upload_coverage:
|
||||||
requires:
|
requires:
|
||||||
- test_cover
|
- test_cover
|
||||||
|
|
||||||
|
|
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -7,16 +7,26 @@ FEATURES:
|
||||||
* Add CacheContext
|
* Add CacheContext
|
||||||
* Add auto sequencing to client
|
* Add auto sequencing to client
|
||||||
* Add FeeHandler to ante handler
|
* Add FeeHandler to ante handler
|
||||||
|
* Gaia stake commands include, DeclareCandidacy, EditCandidacy, Delegate, Unbond
|
||||||
|
* MountStoreWithDB without providing a custom store works.
|
||||||
|
* Repo is now lint compliant / GoMetaLinter with tendermint-lint integrated into CI
|
||||||
|
* Better key output, pubkey go-amino hex bytes now output by default
|
||||||
|
|
||||||
BREAKING CHANGES
|
BREAKING CHANGES
|
||||||
|
|
||||||
* Remove go-wire, use go-amino
|
* Remove go-wire, use go-amino
|
||||||
|
* Gaia simple-staking bond and unbond functions replaced
|
||||||
|
* [stake] Delegator bonds now store the height at which they were updated
|
||||||
* [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
|
||||||
|
* 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
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
|
* Gaia now uses stake, ported from github.com/cosmos/gaia
|
||||||
* MountStoreWithDB without providing a custom store works.
|
|
||||||
|
|
||||||
## 0.14.1 (April 9, 2018)
|
## 0.14.1 (April 9, 2018)
|
||||||
|
|
||||||
|
|
|
@ -342,7 +342,7 @@
|
||||||
"types/priv_validator",
|
"types/priv_validator",
|
||||||
"version"
|
"version"
|
||||||
]
|
]
|
||||||
revision = "d0beaba7e8a5652506a34b5fab299cc2dc274c02"
|
revision = "a2930cd7233f04f5a651020669289296545e70dc"
|
||||||
version = "v0.19.0"
|
version = "v0.19.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
|
@ -384,6 +384,7 @@
|
||||||
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 = "8d16fa6dc9a85c1cd3ed24ad08ff21cf94f10888"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = ["unix"]
|
packages = ["unix"]
|
||||||
revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
|
revision = "b126b21c05a91c856b027c16779c12e3bf236954"
|
||||||
|
|
||||||
[[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 = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "google.golang.org/grpc"
|
name = "google.golang.org/grpc"
|
||||||
|
|
16
Makefile
16
Makefile
|
@ -2,12 +2,12 @@ PACKAGES=$(shell go list ./... | grep -v '/vendor/')
|
||||||
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 build build_examples install install_examples test
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
### CI
|
### CI
|
||||||
|
|
||||||
ci: get_tools get_vendor_deps build test_cover
|
ci: get_tools get_vendor_deps install test_cover
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
### Build
|
### Build
|
||||||
|
@ -15,11 +15,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 +36,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
|
||||||
|
|
|
@ -29,6 +29,7 @@ type BaseApp struct {
|
||||||
db dbm.DB // common DB backend
|
db dbm.DB // common DB backend
|
||||||
cms sdk.CommitMultiStore // Main (uncached) state
|
cms sdk.CommitMultiStore // Main (uncached) state
|
||||||
router Router // handle any kind of message
|
router Router // handle any kind of message
|
||||||
|
codespacer *sdk.Codespacer // handle module codespacing
|
||||||
|
|
||||||
// must be set
|
// must be set
|
||||||
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
|
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
|
||||||
|
@ -56,13 +57,18 @@ 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, 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(),
|
||||||
}
|
}
|
||||||
|
// 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 +76,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 {
|
||||||
|
@ -103,7 +114,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
|
||||||
|
@ -311,9 +321,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 +336,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 +364,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 +451,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{
|
||||||
|
|
|
@ -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{}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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,17 @@ 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)
|
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),
|
||||||
|
@ -54,7 +54,7 @@ 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) {
|
if viper.IsSet(client.FlagSequence) {
|
||||||
return ctx, nil
|
return ctx, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -48,12 +48,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")
|
||||||
|
|
|
@ -42,8 +42,10 @@ func runShowCmd(cmd *cobra.Command, args []string) error {
|
||||||
return err
|
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"]
|
||||||
|
|
|
@ -53,13 +53,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,20 +19,10 @@ import (
|
||||||
// KeyDBName is the directory under root where we store the keys
|
// KeyDBName is the directory under root where we store the keys
|
||||||
const KeyDBName = "keys"
|
const KeyDBName = "keys"
|
||||||
|
|
||||||
var (
|
|
||||||
// keybase is used to make GetKeyBase a singleton
|
// keybase is used to make GetKeyBase a singleton
|
||||||
keybase keys.Keybase
|
var keybase keys.Keybase
|
||||||
)
|
|
||||||
|
|
||||||
// used for outputting keys.Info over REST
|
// initialize a keybase based on the configuration
|
||||||
type KeyOutput struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Address string `json:"address"`
|
|
||||||
// TODO add pubkey?
|
|
||||||
// Pubkey string `json:"pubkey"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetKeyBase initializes a keybase based on the configuration
|
|
||||||
func GetKeyBase() (keys.Keybase, error) {
|
func GetKeyBase() (keys.Keybase, error) {
|
||||||
if keybase == nil {
|
if keybase == nil {
|
||||||
rootDir := viper.GetString(cli.HomeFlag)
|
rootDir := viper.GetString(cli.HomeFlag)
|
||||||
|
@ -47,36 +40,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)
|
||||||
|
}
|
||||||
|
|
|
@ -28,12 +28,11 @@ func waitForRPC() {
|
||||||
_, err := client.Call("status", map[string]interface{}{}, result)
|
_, err := client.Call("status", map[string]interface{}{}, result)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
} else {
|
}
|
||||||
fmt.Println("error", err)
|
fmt.Println("error", err)
|
||||||
time.Sleep(time.Millisecond)
|
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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
|
@ -66,7 +66,7 @@ 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 {
|
||||||
|
|
|
@ -55,6 +55,7 @@ func getBlock(height *int64) ([]byte, error) {
|
||||||
return output, nil
|
return output, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the current blockchain height
|
||||||
func GetChainHeight() (int64, error) {
|
func GetChainHeight() (int64, error) {
|
||||||
node, err := context.NewCoreContextFromViper().GetNode()
|
node, err := context.NewCoreContextFromViper().GetNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -94,6 +95,7 @@ func printBlock(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
// REST
|
// REST
|
||||||
|
|
||||||
|
// REST handler to get a block
|
||||||
func BlockRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func BlockRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
height, err := strconv.ParseInt(vars["height"], 10, 64)
|
height, err := strconv.ParseInt(vars["height"], 10, 64)
|
||||||
|
@ -117,6 +119,7 @@ func BlockRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write(output)
|
w.Write(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// REST handler to get the latest block
|
||||||
func LatestBlockRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func LatestBlockRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
height, err := GetChainHeight()
|
height, err := GetChainHeight()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -44,11 +44,12 @@ func initClientCommand() *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register REST endpoints
|
||||||
func RegisterRoutes(r *mux.Router) {
|
func RegisterRoutes(r *mux.Router) {
|
||||||
r.HandleFunc("/node_info", NodeInfoRequestHandler).Methods("GET")
|
r.HandleFunc("/node_info", NodeInfoRequestHandler).Methods("GET")
|
||||||
r.HandleFunc("/syncing", NodeSyncingRequestHandler).Methods("GET")
|
r.HandleFunc("/syncing", NodeSyncingRequestHandler).Methods("GET")
|
||||||
r.HandleFunc("/blocks/latest", LatestBlockRequestHandler).Methods("GET")
|
r.HandleFunc("/blocks/latest", LatestBlockRequestHandler).Methods("GET")
|
||||||
r.HandleFunc("/blocks/{height}", BlockRequestHandler).Methods("GET")
|
r.HandleFunc("/blocks/{height}", BlockRequestHandler).Methods("GET")
|
||||||
r.HandleFunc("/validatorsets/latest", LatestValidatorsetRequestHandler).Methods("GET")
|
r.HandleFunc("/validatorsets/latest", LatestValidatorSetRequestHandler).Methods("GET")
|
||||||
r.HandleFunc("/validatorsets/{height}", ValidatorsetRequestHandler).Methods("GET")
|
r.HandleFunc("/validatorsets/{height}", ValidatorSetRequestHandler).Methods("GET")
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ func printNodeStatus(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
// REST
|
// REST
|
||||||
|
|
||||||
|
// REST handler for node info
|
||||||
func NodeInfoRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func NodeInfoRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
status, err := getNodeStatus()
|
status, err := getNodeStatus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -69,6 +70,7 @@ func NodeInfoRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write(output)
|
w.Write(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// REST handler for node syncing
|
||||||
func NodeSyncingRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func NodeSyncingRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
status, err := getNodeStatus()
|
status, err := getNodeStatus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -12,6 +12,8 @@ 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>",
|
||||||
|
@ -24,7 +26,7 @@ func validatorCommand() *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetValidators(height *int64) ([]byte, error) {
|
func getValidators(height *int64) ([]byte, error) {
|
||||||
// get the node
|
// get the node
|
||||||
node, err := context.NewCoreContextFromViper().GetNode()
|
node, err := context.NewCoreContextFromViper().GetNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -59,7 +61,7 @@ func printValidators(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := GetValidators(height)
|
output, err := getValidators(height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -70,7 +72,8 @@ func printValidators(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
// REST
|
// REST
|
||||||
|
|
||||||
func ValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
|
// Validator Set at a height REST handler
|
||||||
|
func ValidatorSetRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
height, err := strconv.ParseInt(vars["height"], 10, 64)
|
height, err := strconv.ParseInt(vars["height"], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -84,7 +87,7 @@ func ValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write([]byte("ERROR: Requested block height is bigger then the chain length."))
|
w.Write([]byte("ERROR: Requested block height is bigger then the chain length."))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
output, err := GetValidators(&height)
|
output, err := getValidators(&height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
|
@ -93,14 +96,15 @@ func ValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write(output)
|
w.Write(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LatestValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
|
// Latest Validator Set REST handler
|
||||||
|
func LatestValidatorSetRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
height, err := GetChainHeight()
|
height, err := GetChainHeight()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
output, err := GetValidators(&height)
|
output, err := getValidators(&height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
|
|
|
@ -7,10 +7,12 @@ 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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BroadcastTx REST Handler
|
||||||
func BroadcastTxRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func BroadcastTxRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var m BroadcastTxBody
|
var m BroadcastTxBody
|
||||||
|
|
||||||
|
|
|
@ -21,19 +21,37 @@ 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,
|
RunE: func(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 := queryTx(cdc, 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, 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
|
||||||
|
@ -49,7 +67,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 +106,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
|
||||||
|
|
||||||
|
// transaction query REST handler
|
||||||
func QueryTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Request) {
|
func QueryTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Request) {
|
||||||
c := commander{cdc}
|
|
||||||
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 +119,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, hashHexStr, trustNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
|
|
|
@ -7,23 +7,18 @@ import (
|
||||||
"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),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// register REST routes
|
||||||
func RegisterRoutes(r *mux.Router, cdc *wire.Codec) {
|
func RegisterRoutes(r *mux.Router, cdc *wire.Codec) {
|
||||||
// r.HandleFunc("/txs", SearchTxRequestHandler(cdc)).Methods("GET")
|
|
||||||
r.HandleFunc("/txs/{hash}", QueryTxRequestHandler(cdc)).Methods("GET")
|
r.HandleFunc("/txs/{hash}", QueryTxRequestHandler(cdc)).Methods("GET")
|
||||||
|
// r.HandleFunc("/txs", SearchTxRequestHandler(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(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(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")
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
// Search Tx REST Handler
|
||||||
func SearchTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Request) {
|
func SearchTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Request) {
|
||||||
c := commander{cdc}
|
|
||||||
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(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,200 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Handle fees
|
||||||
|
feeHandler sdk.FeeHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
|
||||||
|
// create your application object
|
||||||
|
var app = &GaiaApp{
|
||||||
|
BaseApp: bam.NewBaseApp(appName, logger, db),
|
||||||
|
cdc: MakeCodec(),
|
||||||
|
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.keyMain, // 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))
|
||||||
|
|
||||||
|
app.Router().
|
||||||
|
AddRoute("bank", bank.NewHandler(app.coinKeeper)).
|
||||||
|
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.coinKeeper)).
|
||||||
|
AddRoute("stake", stake.NewHandler(app.stakeKeeper))
|
||||||
|
|
||||||
|
// Define the feeHandler.
|
||||||
|
app.feeHandler = auth.BurnFeeHandler
|
||||||
|
|
||||||
|
// initialize BaseApp
|
||||||
|
app.SetTxDecoder(app.txDecoder)
|
||||||
|
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, app.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()
|
||||||
|
|
||||||
|
// Register Msgs
|
||||||
|
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||||
|
|
||||||
|
ibc.RegisterWire(cdc)
|
||||||
|
bank.RegisterWire(cdc)
|
||||||
|
stake.RegisterWire(cdc)
|
||||||
|
|
||||||
|
// Register AppAccount
|
||||||
|
cdc.RegisterInterface((*sdk.Account)(nil), nil)
|
||||||
|
cdc.RegisterConcrete(&auth.BaseAccount{}, "gaia/Account", nil)
|
||||||
|
|
||||||
|
// Register crypto.
|
||||||
|
wire.RegisterCrypto(cdc)
|
||||||
|
|
||||||
|
return cdc
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom logic for transaction decoding
|
||||||
|
func (app *GaiaApp) 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
|
||||||
|
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, sdk.ErrTxDecode("").Trace(err.Error())
|
||||||
|
}
|
||||||
|
return tx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom logic for gaia initialization
|
||||||
|
func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||||
|
stateJSON := req.AppStateBytes
|
||||||
|
|
||||||
|
genesisState := new(GenesisState)
|
||||||
|
err := json.Unmarshal(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{}
|
||||||
|
}
|
||||||
|
|
||||||
|
//__________________________________________________________
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert GenesisAccount to GaiaAccount
|
||||||
|
func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) {
|
||||||
|
return &auth.BaseAccount{
|
||||||
|
Address: ga.Address,
|
||||||
|
Coins: ga.Coins.Sort(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultGenAppState expects two args: an account address
|
||||||
|
// and a coin denomination, and gives lots of coins to that address.
|
||||||
|
func DefaultGenAppState(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
|
||||||
|
|
||||||
|
accAuth := auth.NewBaseAccountWithAddress(addr)
|
||||||
|
accAuth.Coins = sdk.Coins{{"fermion", 100000}}
|
||||||
|
acc := NewGenesisAccount(&accAuth)
|
||||||
|
genaccs := []GenesisAccount{acc}
|
||||||
|
|
||||||
|
genesisState := GenesisState{
|
||||||
|
Accounts: genaccs,
|
||||||
|
StakeData: stake.GetDefaultGenesisState(),
|
||||||
|
}
|
||||||
|
|
||||||
|
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateBytes, nil
|
||||||
|
}
|
|
@ -0,0 +1,518 @@
|
||||||
|
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/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 := 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 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("42fermion")
|
||||||
|
require.Nil(t, err)
|
||||||
|
bondCoin, err := sdk.ParseCoin("10fermion")
|
||||||
|
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,172 @@
|
||||||
|
package clitest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"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/x/auth"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGaiaCLISend(t *testing.T) {
|
||||||
|
|
||||||
|
tests.ExecuteT(t, "gaiad unsafe_reset_all", 1)
|
||||||
|
pass := "1234567890"
|
||||||
|
executeWrite(t, "gaiacli keys delete foo", pass)
|
||||||
|
executeWrite(t, "gaiacli keys delete bar", pass)
|
||||||
|
masterKey, chainID := executeInit(t, "gaiad init")
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
|
||||||
|
executeWrite(t, "gaiacli keys add foo --recover", pass, masterKey)
|
||||||
|
executeWrite(t, "gaiacli keys add bar", pass)
|
||||||
|
|
||||||
|
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(100000), fooAcc.GetCoins().AmountOf("fermion"))
|
||||||
|
|
||||||
|
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10fermion --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("fermion"))
|
||||||
|
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, flags))
|
||||||
|
assert.Equal(t, int64(99990), fooAcc.GetCoins().AmountOf("fermion"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGaiaCLIDeclareCandidacy(t *testing.T) {
|
||||||
|
|
||||||
|
tests.ExecuteT(t, "gaiad unsafe_reset_all", 1)
|
||||||
|
pass := "1234567890"
|
||||||
|
executeWrite(t, "gaiacli keys delete foo", pass)
|
||||||
|
masterKey, chainID := executeInit(t, "gaiad init")
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
|
||||||
|
executeWrite(t, "gaiacli keys add foo --recover", pass, masterKey)
|
||||||
|
fooAddr, fooPubKey := executeGetAddrPK(t, "gaiacli keys show foo --output=json")
|
||||||
|
fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, flags))
|
||||||
|
assert.Equal(t, int64(100000), fooAcc.GetCoins().AmountOf("fermion"))
|
||||||
|
|
||||||
|
// declare candidacy
|
||||||
|
declStr := fmt.Sprintf("gaiacli declare-candidacy %v", flags)
|
||||||
|
declStr += fmt.Sprintf(" --name=%v", "foo")
|
||||||
|
declStr += fmt.Sprintf(" --address-candidate=%v", fooAddr)
|
||||||
|
declStr += fmt.Sprintf(" --pubkey=%v", fooPubKey)
|
||||||
|
declStr += fmt.Sprintf(" --amount=%v", "3fermion")
|
||||||
|
declStr += fmt.Sprintf(" --moniker=%v", "foo-vally")
|
||||||
|
fmt.Printf("debug declStr: %v\n", declStr)
|
||||||
|
executeWrite(t, declStr, 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(99997), fooAcc.GetCoins().AmountOf("fermion"))
|
||||||
|
candidate := executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, fooAddr))
|
||||||
|
assert.Equal(t, candidate.Address.String(), fooAddr)
|
||||||
|
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", "foo")
|
||||||
|
unbondStr += fmt.Sprintf(" --address-candidate=%v", fooAddr)
|
||||||
|
unbondStr += fmt.Sprintf(" --address-delegator=%v", fooAddr)
|
||||||
|
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
|
||||||
|
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", fooAddr, flags))
|
||||||
|
assert.Equal(t, int64(99998), fooAcc.GetCoins().AmountOf("fermion"))
|
||||||
|
candidate = executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, fooAddr))
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
cmd.Wait()
|
||||||
|
|
||||||
|
bz := make([]byte, 100000)
|
||||||
|
rc.Read(bz)
|
||||||
|
fmt.Printf("debug read: %v\n", string(bz))
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeInit(t *testing.T, cmdStr string) (masterKey, chainID string) {
|
||||||
|
out := tests.ExecuteT(t, cmdStr, 1)
|
||||||
|
outCut := "{" + strings.SplitN(out, "{", 2)[1] // weird I'm sorry
|
||||||
|
|
||||||
|
var initRes map[string]json.RawMessage
|
||||||
|
err := json.Unmarshal([]byte(outCut), &initRes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = json.Unmarshal(initRes["secret"], &masterKey)
|
||||||
|
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, 2)
|
||||||
|
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, 2)
|
||||||
|
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
|
||||||
|
_ = json.Unmarshal(value, &acc) //XXX pubkey can't be decoded go amino issue
|
||||||
|
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, 2)
|
||||||
|
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
|
||||||
|
}
|
|
@ -12,19 +12,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/commands"
|
||||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
|
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
|
||||||
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands"
|
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands"
|
||||||
simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands"
|
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/commands"
|
||||||
|
|
||||||
"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 +30,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 +46,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("main", 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 +73,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", os.ExpandEnv("$HOME/.gaiacli"))
|
||||||
executor.Execute()
|
executor.Execute()
|
||||||
}
|
}
|
|
@ -11,7 +11,7 @@ import (
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,21 +25,21 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: distinguish from basecoin
|
|
||||||
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||||
dataDir := filepath.Join(rootDir, "data")
|
dataDir := filepath.Join(rootDir, "data")
|
||||||
db, err := dbm.NewGoLevelDB("gaia", dataDir)
|
db, err := dbm.NewGoLevelDB("gaia", dataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
bapp := app.NewBasecoinApp(logger, db)
|
bapp := app.NewGaiaApp(logger, db)
|
||||||
return bapp, nil
|
return bapp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context)
|
server.AddCommands(rootCmd, app.DefaultGenAppState, generateApp, context)
|
||||||
|
|
||||||
// prepare and add flags
|
// prepare and add flags
|
||||||
executor := cli.PrepareBaseCmd(rootCmd, "GA", os.ExpandEnv("$HOME/.gaiad"))
|
rootDir := os.ExpandEnv("$HOME/.gaiad")
|
||||||
|
executor := cli.PrepareBaseCmd(rootCmd, "GA", rootDir)
|
||||||
executor.Execute()
|
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'
|
||||||
|
|
|
@ -69,12 +69,12 @@ 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 +83,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 +91,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 +174,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 +319,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
|
|
||||||
|
|
|
@ -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)
|
|
@ -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": "fermion",
|
||||||
|
"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": "fermion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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,193 @@
|
||||||
|
Testnet Setup
|
||||||
|
=============
|
||||||
|
|
||||||
|
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 new key with a name, and save the address:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
MYNAME=<your name>
|
||||||
|
gaiacli keys new $MYNAME
|
||||||
|
gaiacli keys list
|
||||||
|
MYADDR=<your newly generated address>
|
||||||
|
|
||||||
|
|
||||||
|
Now initialize a gaia chain:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gaiad init --home=$HOME/.gaiad1
|
||||||
|
|
||||||
|
you should see seed phrase for genesis account in the output & config & data folder in the home directory.
|
||||||
|
|
||||||
|
In the config folder, there will be the following files: ``config.toml``, ``genesis.json``, ``node_key.json``, and ``priv_validator.json``.
|
||||||
|
|
||||||
|
The genesis file should look like this:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
{
|
||||||
|
"genesis_time": "0001-01-01T00:00:00Z",
|
||||||
|
"chain_id": "test-chain-0TRiTa",
|
||||||
|
"validators": [
|
||||||
|
{
|
||||||
|
"pub_key": {
|
||||||
|
"type": "AC26791624DE60",
|
||||||
|
"value": "<value>"
|
||||||
|
},
|
||||||
|
"power": 10,
|
||||||
|
"name": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"app_hash": "",
|
||||||
|
"app_state": {
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"address": "<ADDR>",
|
||||||
|
"coins": [
|
||||||
|
{
|
||||||
|
"denom": "steak",
|
||||||
|
"amount": 9007199254740992
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
**Note:** We need to change the denomination of token from default to ``steak`` in the genesis file.
|
||||||
|
|
||||||
|
Then, recover the genesis account with ``gaiacli``:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gaiacli keys add <name> --recover
|
||||||
|
|
||||||
|
By now, you have set up the first node. This is great!
|
||||||
|
|
||||||
|
We can add a second node on our local machine by initiating a node in a new directory, and copying in the ``genesis.json``:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gaiad init --home=$HOME/.gaiad2
|
||||||
|
|
||||||
|
and replace the ``genesis.json`` and ``config.toml`` files:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
cp $HOME/.gaiad/config/genesis.json $HOME/.gaiad2/config
|
||||||
|
cp $HOME/.gaiad/config/config.toml $HOME/.gaiad2/config
|
||||||
|
|
||||||
|
then, get the node id of first node:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gaiad show_node_id --home=$HOME/.gaiad1
|
||||||
|
|
||||||
|
We need to also modify $HOME/.gaiad2/config.toml to set new seeds and ports. It should look like:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
proxy_app = "tcp://127.0.0.1:46668"
|
||||||
|
moniker = "anonymous"
|
||||||
|
fast_sync = true
|
||||||
|
db_backend = "leveldb"
|
||||||
|
log_level = "state:info,*:error"
|
||||||
|
|
||||||
|
[rpc]
|
||||||
|
laddr = "tcp://0.0.0.0:46667"
|
||||||
|
|
||||||
|
[p2p]
|
||||||
|
laddr = "tcp://0.0.0.0:46666"
|
||||||
|
persistent_peers = "<node1-ID>@0.0.0.0:46656"
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
There is only **one** validator now. Let's add another one!
|
||||||
|
|
||||||
|
First, we need to create a new account:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gaiacli keys new <NAME>
|
||||||
|
|
||||||
|
Check that we now have two accounts:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gaiacli keys list
|
||||||
|
|
||||||
|
Then, we try to transfer some ``steak`` to another account:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gaiacli send --amount=1000steak --to=$MYADDR2 --name=$NAME --chain-id=<CHAIN-ID> --node=tcp://localhost:46657 --sequence=0
|
||||||
|
|
||||||
|
**Note:** We need to be careful with the ``chain-id`` and ``sequence``
|
||||||
|
|
||||||
|
Check the balance & sequence with:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gaiacli account $MYADDR
|
||||||
|
|
||||||
|
We can see the balance of ``$MYADDR2`` is 1000 now.
|
||||||
|
|
||||||
|
Finally, let's bond the validator in ``$HOME/gaiad2``. Get the pubkey first:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
cat $HOME/.gaiad2/config/priv_validator.json | jq .pub_key.value
|
||||||
|
|
||||||
|
Go to `this website <http://tomeko.net/online_tools/base64.php?lang=en>`__ to change pubkey from base64 to Hex.
|
||||||
|
|
||||||
|
Ok, now we can bond some coins to that pubkey:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gaiacli bond --stake=1steak --validator=<validator-pubkey-hex> --sequence=0 --chain-id=<chain-id> --name=test
|
||||||
|
|
||||||
|
Nice. We can see there are now two validators:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gaiacli validatorset
|
||||||
|
|
||||||
|
Check the balance of ``$MYADDR2`` to see the difference: it has 1 less ``steak``!
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gaiacli account $MYADDR2
|
||||||
|
|
||||||
|
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 --sequence=# --chain-id=<chain-id> --name=test
|
||||||
|
|
||||||
|
That's it!
|
|
@ -61,12 +61,12 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
||||||
cdc,
|
cdc,
|
||||||
app.capKeyMainStore, // target store
|
app.capKeyMainStore, // target store
|
||||||
&types.AppAccount{}, // prototype
|
&types.AppAccount{}, // prototype
|
||||||
).Seal()
|
)
|
||||||
|
|
||||||
// Add handlers.
|
// Add handlers.
|
||||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
coinKeeper := bank.NewKeeper(app.accountMapper)
|
||||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
ibcMapper := ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace))
|
||||||
app.Router().
|
app.Router().
|
||||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
||||||
|
@ -94,12 +94,12 @@ func MakeCodec() *wire.Codec {
|
||||||
|
|
||||||
// Register Msgs
|
// Register Msgs
|
||||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||||
cdc.RegisterConcrete(bank.SendMsg{}, "basecoin/Send", nil)
|
cdc.RegisterConcrete(bank.MsgSend{}, "basecoin/Send", nil)
|
||||||
cdc.RegisterConcrete(bank.IssueMsg{}, "basecoin/Issue", nil)
|
cdc.RegisterConcrete(bank.MsgIssue{}, "basecoin/Issue", nil)
|
||||||
cdc.RegisterConcrete(ibc.IBCTransferMsg{}, "basecoin/IBCTransferMsg", nil)
|
cdc.RegisterConcrete(ibc.IBCTransferMsg{}, "basecoin/IBCTransferMsg", nil)
|
||||||
cdc.RegisterConcrete(ibc.IBCReceiveMsg{}, "basecoin/IBCReceiveMsg", nil)
|
cdc.RegisterConcrete(ibc.IBCReceiveMsg{}, "basecoin/IBCReceiveMsg", nil)
|
||||||
cdc.RegisterConcrete(simplestake.BondMsg{}, "basecoin/BondMsg", nil)
|
cdc.RegisterConcrete(simplestake.MsgBond{}, "basecoin/BondMsg", nil)
|
||||||
cdc.RegisterConcrete(simplestake.UnbondMsg{}, "basecoin/UnbondMsg", nil)
|
cdc.RegisterConcrete(simplestake.MsgUnbond{}, "basecoin/UnbondMsg", nil)
|
||||||
|
|
||||||
// Register AppAccount
|
// Register AppAccount
|
||||||
cdc.RegisterInterface((*sdk.Account)(nil), nil)
|
cdc.RegisterInterface((*sdk.Account)(nil), nil)
|
||||||
|
@ -123,7 +123,7 @@ func (app *BasecoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||||
// are registered by MakeTxCodec in bank.RegisterAmino.
|
// are registered by MakeTxCodec in bank.RegisterAmino.
|
||||||
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
|
return nil, sdk.ErrTxDecode("").Trace(err.Error())
|
||||||
}
|
}
|
||||||
return tx, nil
|
return tx, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -66,14 +66,14 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
|
||||||
cdc,
|
cdc,
|
||||||
app.capKeyMainStore, // target store
|
app.capKeyMainStore, // target store
|
||||||
&types.AppAccount{}, // prototype
|
&types.AppAccount{}, // prototype
|
||||||
).Seal()
|
)
|
||||||
|
|
||||||
// Add handlers.
|
// Add handlers.
|
||||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
coinKeeper := bank.NewKeeper(app.accountMapper)
|
||||||
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
|
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper, app.RegisterCodespace(cool.DefaultCodespace))
|
||||||
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper)
|
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), coinKeeper, app.RegisterCodespace(pow.DefaultCodespace))
|
||||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
ibcMapper := ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace))
|
||||||
app.Router().
|
app.Router().
|
||||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||||
AddRoute("cool", cool.NewHandler(coolKeeper)).
|
AddRoute("cool", cool.NewHandler(coolKeeper)).
|
||||||
|
@ -104,15 +104,15 @@ func MakeCodec() *wire.Codec {
|
||||||
|
|
||||||
// Register Msgs
|
// Register Msgs
|
||||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||||
cdc.RegisterConcrete(bank.SendMsg{}, "democoin/Send", nil)
|
cdc.RegisterConcrete(bank.MsgSend{}, "democoin/Send", nil)
|
||||||
cdc.RegisterConcrete(bank.IssueMsg{}, "democoin/Issue", nil)
|
cdc.RegisterConcrete(bank.MsgIssue{}, "democoin/Issue", nil)
|
||||||
cdc.RegisterConcrete(cool.QuizMsg{}, "democoin/Quiz", nil)
|
cdc.RegisterConcrete(cool.MsgQuiz{}, "democoin/Quiz", nil)
|
||||||
cdc.RegisterConcrete(cool.SetTrendMsg{}, "democoin/SetTrend", nil)
|
cdc.RegisterConcrete(cool.MsgSetTrend{}, "democoin/SetTrend", nil)
|
||||||
cdc.RegisterConcrete(pow.MineMsg{}, "democoin/Mine", nil)
|
cdc.RegisterConcrete(pow.MsgMine{}, "democoin/Mine", nil)
|
||||||
cdc.RegisterConcrete(ibc.IBCTransferMsg{}, "democoin/IBCTransferMsg", nil)
|
cdc.RegisterConcrete(ibc.IBCTransferMsg{}, "democoin/IBCTransferMsg", nil)
|
||||||
cdc.RegisterConcrete(ibc.IBCReceiveMsg{}, "democoin/IBCReceiveMsg", nil)
|
cdc.RegisterConcrete(ibc.IBCReceiveMsg{}, "democoin/IBCReceiveMsg", nil)
|
||||||
cdc.RegisterConcrete(simplestake.BondMsg{}, "democoin/BondMsg", nil)
|
cdc.RegisterConcrete(simplestake.MsgBond{}, "democoin/BondMsg", nil)
|
||||||
cdc.RegisterConcrete(simplestake.UnbondMsg{}, "democoin/UnbondMsg", nil)
|
cdc.RegisterConcrete(simplestake.MsgUnbond{}, "democoin/UnbondMsg", nil)
|
||||||
|
|
||||||
// Register AppAccount
|
// Register AppAccount
|
||||||
cdc.RegisterInterface((*sdk.Account)(nil), nil)
|
cdc.RegisterInterface((*sdk.Account)(nil), nil)
|
||||||
|
@ -136,7 +136,7 @@ func (app *DemocoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||||
// are registered by MakeTxCodec in bank.RegisterWire.
|
// are registered by MakeTxCodec in bank.RegisterWire.
|
||||||
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
|
return nil, sdk.ErrTxDecode("").Trace(err.Error())
|
||||||
}
|
}
|
||||||
return tx, nil
|
return tx, nil
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep
|
||||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = powKeeper.InitGenesis(ctx, genesisState.PowGenesis)
|
err = powKeeper.InitGenesis(ctx, 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, "")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -34,19 +34,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
|
||||||
}
|
}
|
||||||
|
@ -78,17 +72,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,7 @@ 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 (k Keeper) InitGenesis(ctx sdk.Context, data Genesis) error {
|
||||||
k.setTrend(ctx, data.Trend)
|
k.setTrend(ctx, data.Trend)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,39 +8,39 @@ 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) Get(key interface{}) (value interface{}) { return nil }
|
||||||
func (msg SetTrendMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
func (msg MsgSetTrend) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||||
func (msg SetTrendMsg) String() string {
|
func (msg MsgSetTrend) String() string {
|
||||||
return fmt.Sprintf("SetTrendMsg{Sender: %v, Cool: %v}", msg.Sender, msg.Cool)
|
return fmt.Sprintf("MsgSetTrend{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 +54,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 +66,32 @@ 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) Get(key interface{}) (value interface{}) { return nil }
|
||||||
func (msg QuizMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
func (msg MsgQuiz) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||||
func (msg QuizMsg) String() string {
|
func (msg MsgQuiz) String() string {
|
||||||
return fmt.Sprintf("QuizMsg{Sender: %v, CoolAnswer: %v}", msg.Sender, msg.CoolAnswer)
|
return fmt.Sprintf("MsgQuiz{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 +99,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)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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]",
|
||||||
|
@ -36,12 +37,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 +48,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,9 +4,12 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO remove, seems hacky
|
||||||
type CodeType = sdk.CodeType
|
type CodeType = sdk.CodeType
|
||||||
|
|
||||||
|
// POW errors reserve 200 ~ 299
|
||||||
const (
|
const (
|
||||||
|
DefaultCodespace sdk.CodespaceType = 5
|
||||||
CodeInvalidDifficulty CodeType = 201
|
CodeInvalidDifficulty CodeType = 201
|
||||||
CodeNonexistentDifficulty CodeType = 202
|
CodeNonexistentDifficulty CodeType = 202
|
||||||
CodeNonexistentReward CodeType = 203
|
CodeNonexistentReward CodeType = 203
|
||||||
|
@ -40,43 +43,37 @@ func codeToDefaultMsg(code CodeType) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ErrInvalidDifficulty(msg string) sdk.Error {
|
// nolint
|
||||||
return newError(CodeInvalidDifficulty, msg)
|
func ErrInvalidDifficulty(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||||
|
return newError(codespace, CodeInvalidDifficulty, msg)
|
||||||
}
|
}
|
||||||
|
func ErrNonexistentDifficulty(codespace sdk.CodespaceType) sdk.Error {
|
||||||
func ErrNonexistentDifficulty() sdk.Error {
|
return newError(codespace, CodeNonexistentDifficulty, "")
|
||||||
return newError(CodeNonexistentDifficulty, "")
|
|
||||||
}
|
}
|
||||||
|
func ErrNonexistentReward(codespace sdk.CodespaceType) sdk.Error {
|
||||||
func ErrNonexistentReward() sdk.Error {
|
return newError(codespace, CodeNonexistentReward, "")
|
||||||
return newError(CodeNonexistentReward, "")
|
|
||||||
}
|
}
|
||||||
|
func ErrNonexistentCount(codespace sdk.CodespaceType) sdk.Error {
|
||||||
func ErrNonexistentCount() sdk.Error {
|
return newError(codespace, CodeNonexistentCount, "")
|
||||||
return newError(CodeNonexistentCount, "")
|
|
||||||
}
|
}
|
||||||
|
func ErrInvalidProof(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||||
func ErrInvalidProof(msg string) sdk.Error {
|
return newError(codespace, CodeInvalidProof, msg)
|
||||||
return newError(CodeInvalidProof, msg)
|
|
||||||
}
|
}
|
||||||
|
func ErrNotBelowTarget(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||||
func ErrNotBelowTarget(msg string) sdk.Error {
|
return newError(codespace, CodeNotBelowTarget, msg)
|
||||||
return newError(CodeNotBelowTarget, msg)
|
|
||||||
}
|
}
|
||||||
|
func ErrInvalidCount(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||||
func ErrInvalidCount(msg string) sdk.Error {
|
return newError(codespace, CodeInvalidCount, msg)
|
||||||
return newError(CodeInvalidCount, msg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func msgOrDefaultMsg(msg string, code CodeType) string {
|
func msgOrDefaultMsg(msg string, code CodeType) string {
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
return msg
|
return msg
|
||||||
} else {
|
}
|
||||||
return codeToDefaultMsg(code)
|
return codeToDefaultMsg(code)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func newError(code CodeType, msg string) sdk.Error {
|
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
|
||||||
msg = msgOrDefaultMsg(msg, code)
|
msg = msgOrDefaultMsg(msg, code)
|
||||||
return sdk.NewError(code, msg)
|
return sdk.NewError(codespace, code, msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@ func TestPowHandler(t *testing.T) {
|
||||||
|
|
||||||
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)
|
||||||
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 +30,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 := keeper.InitGenesis(ctx, 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 +51,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,45 @@ 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 {
|
// Init Genessis for the POW module
|
||||||
pk.SetLastDifficulty(ctx, genesis.Difficulty)
|
func (k Keeper) InitGenesis(ctx sdk.Context, genesis Genesis) error {
|
||||||
pk.SetLastCount(ctx, genesis.Count)
|
k.SetLastDifficulty(ctx, genesis.Difficulty)
|
||||||
|
k.SetLastCount(ctx, genesis.Count)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +56,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 +75,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
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,11 +33,11 @@ func TestPowKeeperGetSet(t *testing.T) {
|
||||||
|
|
||||||
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)
|
||||||
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 := keeper.InitGenesis(ctx, Genesis{uint64(1), uint64(0)})
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
res, err := keeper.GetLastDifficulty(ctx)
|
res, err := keeper.GetLastDifficulty(ctx)
|
||||||
|
|
|
@ -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,23 @@ 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) Get(key interface{}) (value interface{}) { return nil }
|
||||||
func (msg MineMsg) String() string {
|
func (msg MsgMine) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||||
return fmt.Sprintf("MineMsg{Sender: %v, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
|
func (msg MsgMine) String() string {
|
||||||
|
return fmt.Sprintf("MsgMine{Sender: %v, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg MineMsg) ValidateBasic() sdk.Error {
|
// validate the mine message
|
||||||
|
func (msg MsgMine) ValidateBasic() sdk.Error {
|
||||||
// check hash
|
// 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 +54,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 +62,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,72 @@ 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 TestMsgMineGet(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("")}
|
||||||
res := msg.Get(nil)
|
res := msg.Get(nil)
|
||||||
assert.Nil(t, res)
|
assert.Nil(t, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMineMsgGetSignBytes(t *testing.T) {
|
func TestMsgMineGetSignBytes(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.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]")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -70,16 +70,16 @@ func (c initCmd) run(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate secrete and address
|
// generate secret and address
|
||||||
addr, secret, err := GenerateCoinKey()
|
addr, secret, err := GenerateCoinKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var DEFAULT_DENOM = "mycoin"
|
var defaultDenom = "mycoin"
|
||||||
|
|
||||||
// Now, we want to add the custom app_state
|
// Now, we want to add the custom app_state
|
||||||
appState, err := c.genAppState(args, addr, DEFAULT_DENOM)
|
appState, err := c.genAppState(args, addr, defaultDenom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
|
@ -12,73 +15,59 @@ import (
|
||||||
|
|
||||||
// ShowNodeIDCmd - ported from Tendermint, dump node ID to stdout
|
// ShowNodeIDCmd - ported from Tendermint, dump node ID to stdout
|
||||||
func ShowNodeIDCmd(ctx *Context) *cobra.Command {
|
func ShowNodeIDCmd(ctx *Context) *cobra.Command {
|
||||||
cmd := showNodeId{ctx}
|
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: "show_node_id",
|
Use: "show_node_id",
|
||||||
Short: "Show this node's ID",
|
Short: "Show this node's ID",
|
||||||
RunE: cmd.run,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
}
|
cfg := ctx.Config
|
||||||
}
|
|
||||||
|
|
||||||
type showNodeId struct {
|
|
||||||
context *Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s showNodeId) run(cmd *cobra.Command, args []string) error {
|
|
||||||
cfg := s.context.Config
|
|
||||||
nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile())
|
nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println(nodeKey.ID())
|
fmt.Println(nodeKey.ID())
|
||||||
return nil
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// ShowValidator - ported from Tendermint, show this node's validator info
|
// ShowValidator - ported from Tendermint, show this node's validator info
|
||||||
func ShowValidatorCmd(ctx *Context) *cobra.Command {
|
func ShowValidatorCmd(ctx *Context) *cobra.Command {
|
||||||
cmd := showValidator{ctx}
|
flagJSON := "json"
|
||||||
return &cobra.Command{
|
cmd := cobra.Command{
|
||||||
Use: "show_validator",
|
Use: "show_validator",
|
||||||
Short: "Show this node's validator info",
|
Short: "Show this node's validator info",
|
||||||
RunE: cmd.run,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type showValidator struct {
|
cfg := ctx.Config
|
||||||
context *Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s showValidator) run(cmd *cobra.Command, args []string) error {
|
|
||||||
cfg := s.context.Config
|
|
||||||
privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorFile())
|
privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorFile())
|
||||||
pubKeyJSONBytes, err := cdc.MarshalJSON(privValidator.PubKey)
|
pubKey := privValidator.PubKey
|
||||||
|
|
||||||
|
if viper.GetBool(flagJSON) {
|
||||||
|
pubKeyJSONBytes, err := cdc.MarshalJSON(pubKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println(string(pubKeyJSONBytes))
|
fmt.Println(string(pubKeyJSONBytes))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
pubKeyHex := strings.ToUpper(hex.EncodeToString(pubKey.Bytes()))
|
||||||
//------------------------------------------------------------------------------
|
fmt.Println(pubKeyHex)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.Flags().Bool(flagJSON, false, "get machine parseable output")
|
||||||
|
return &cmd
|
||||||
|
}
|
||||||
|
|
||||||
// UnsafeResetAllCmd - extension of the tendermint command, resets initialization
|
// UnsafeResetAllCmd - extension of the tendermint command, resets initialization
|
||||||
func UnsafeResetAllCmd(ctx *Context) *cobra.Command {
|
func UnsafeResetAllCmd(ctx *Context) *cobra.Command {
|
||||||
cmd := resetAll{ctx}
|
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: "unsafe_reset_all",
|
Use: "unsafe_reset_all",
|
||||||
Short: "Reset all blockchain data",
|
Short: "Reset all blockchain data",
|
||||||
RunE: cmd.run,
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
}
|
cfg := ctx.Config
|
||||||
}
|
tcmd.ResetAll(cfg.DBDir(), cfg.PrivValidatorFile(), ctx.Logger)
|
||||||
|
|
||||||
type resetAll struct {
|
|
||||||
context *Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r resetAll) run(cmd *cobra.Command, args []string) error {
|
|
||||||
cfg := r.context.Config
|
|
||||||
tcmd.ResetAll(cfg.DBDir(), cfg.PrivValidatorFile(), r.context.Logger)
|
|
||||||
return nil
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// server context
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Config *cfg.Config
|
Config *cfg.Config
|
||||||
Logger log.Logger
|
Logger log.Logger
|
||||||
|
@ -59,6 +60,7 @@ func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add server commands
|
||||||
func AddCommands(
|
func AddCommands(
|
||||||
rootCmd *cobra.Command,
|
rootCmd *cobra.Command,
|
||||||
appState GenAppState, appCreator AppCreator,
|
appState GenAppState, appCreator AppCreator,
|
||||||
|
|
|
@ -26,13 +26,12 @@ type cacheKVStore struct {
|
||||||
|
|
||||||
var _ CacheKVStore = (*cacheKVStore)(nil)
|
var _ CacheKVStore = (*cacheKVStore)(nil)
|
||||||
|
|
||||||
|
// nolint
|
||||||
func NewCacheKVStore(parent KVStore) *cacheKVStore {
|
func NewCacheKVStore(parent KVStore) *cacheKVStore {
|
||||||
|
|
||||||
ci := &cacheKVStore{
|
ci := &cacheKVStore{
|
||||||
cache: make(map[string]cValue),
|
cache: make(map[string]cValue),
|
||||||
parent: parent,
|
parent: parent,
|
||||||
}
|
}
|
||||||
|
|
||||||
return ci
|
return ci
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,9 +169,8 @@ func (ci *cacheKVStore) dirtyItems(ascending bool) []cmn.KVPair {
|
||||||
sort.Slice(items, func(i, j int) bool {
|
sort.Slice(items, func(i, j int) bool {
|
||||||
if ascending {
|
if ascending {
|
||||||
return bytes.Compare(items[i].Key, items[j].Key) < 0
|
return bytes.Compare(items[i].Key, items[j].Key) < 0
|
||||||
} else {
|
|
||||||
return bytes.Compare(items[i].Key, items[j].Key) > 0
|
|
||||||
}
|
}
|
||||||
|
return bytes.Compare(items[i].Key, items[j].Key) > 0
|
||||||
})
|
})
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ func TestCacheKVIteratorBounds(t *testing.T) {
|
||||||
k, v := itr.Key(), itr.Value()
|
k, v := itr.Key(), itr.Value()
|
||||||
assert.Equal(t, keyFmt(i), k)
|
assert.Equal(t, keyFmt(i), k)
|
||||||
assert.Equal(t, valFmt(i), v)
|
assert.Equal(t, valFmt(i), v)
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, nItems, i)
|
assert.Equal(t, nItems, i)
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ func TestCacheKVIteratorBounds(t *testing.T) {
|
||||||
itr = st.Iterator(bz("money"), nil)
|
itr = st.Iterator(bz("money"), nil)
|
||||||
i = 0
|
i = 0
|
||||||
for ; itr.Valid(); itr.Next() {
|
for ; itr.Valid(); itr.Next() {
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, 0, i)
|
assert.Equal(t, 0, i)
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ func TestCacheKVIteratorBounds(t *testing.T) {
|
||||||
k, v := itr.Key(), itr.Value()
|
k, v := itr.Key(), itr.Value()
|
||||||
assert.Equal(t, keyFmt(i), k)
|
assert.Equal(t, keyFmt(i), k)
|
||||||
assert.Equal(t, valFmt(i), v)
|
assert.Equal(t, valFmt(i), v)
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, 3, i)
|
assert.Equal(t, 3, i)
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ func TestCacheKVIteratorBounds(t *testing.T) {
|
||||||
k, v := itr.Key(), itr.Value()
|
k, v := itr.Key(), itr.Value()
|
||||||
assert.Equal(t, keyFmt(i), k)
|
assert.Equal(t, keyFmt(i), k)
|
||||||
assert.Equal(t, valFmt(i), v)
|
assert.Equal(t, valFmt(i), v)
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, 4, i)
|
assert.Equal(t, 4, i)
|
||||||
}
|
}
|
||||||
|
@ -369,7 +369,7 @@ func assertIterateDomain(t *testing.T, st KVStore, expectedN int) {
|
||||||
k, v := itr.Key(), itr.Value()
|
k, v := itr.Key(), itr.Value()
|
||||||
assert.Equal(t, keyFmt(i), k)
|
assert.Equal(t, keyFmt(i), k)
|
||||||
assert.Equal(t, valFmt(i), v)
|
assert.Equal(t, valFmt(i), v)
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, expectedN, i)
|
assert.Equal(t, expectedN, i)
|
||||||
}
|
}
|
||||||
|
@ -397,7 +397,7 @@ func assertIterateDomainCheck(t *testing.T, st KVStore, mem dbm.DB, r []keyRange
|
||||||
|
|
||||||
itr.Next()
|
itr.Next()
|
||||||
itr2.Next()
|
itr2.Next()
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.False(t, itr.Valid())
|
assert.False(t, itr.Valid())
|
||||||
|
@ -479,10 +479,10 @@ func (krc *keyRangeCounter) valid() bool {
|
||||||
func (krc *keyRangeCounter) next() {
|
func (krc *keyRangeCounter) next() {
|
||||||
thisKeyRange := krc.keyRanges[krc.rangeIdx]
|
thisKeyRange := krc.keyRanges[krc.rangeIdx]
|
||||||
if krc.idx == thisKeyRange.len()-1 {
|
if krc.idx == thisKeyRange.len()-1 {
|
||||||
krc.rangeIdx += 1
|
krc.rangeIdx++
|
||||||
krc.idx = 0
|
krc.idx = 0
|
||||||
} else {
|
} else {
|
||||||
krc.idx += 1
|
krc.idx++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,9 +151,8 @@ func (iter *cacheMergeIterator) Close() {
|
||||||
func (iter *cacheMergeIterator) compare(a, b []byte) int {
|
func (iter *cacheMergeIterator) compare(a, b []byte) int {
|
||||||
if iter.ascending {
|
if iter.ascending {
|
||||||
return bytes.Compare(a, b)
|
return bytes.Compare(a, b)
|
||||||
} else {
|
|
||||||
return bytes.Compare(a, b) * -1
|
|
||||||
}
|
}
|
||||||
|
return bytes.Compare(a, b) * -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip all delete-items from the cache w/ `key < until`. After this function,
|
// Skip all delete-items from the cache w/ `key < until`. After this function,
|
||||||
|
|
|
@ -10,7 +10,7 @@ type dbStoreAdapter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Store.
|
// Implements Store.
|
||||||
func (_ dbStoreAdapter) GetStoreType() StoreType {
|
func (dbStoreAdapter) GetStoreType() StoreType {
|
||||||
return sdk.StoreTypeDB
|
return sdk.StoreTypeDB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,9 +23,8 @@ func Last(st KVStore, start, end []byte) (kv cmn.KVPair, ok bool) {
|
||||||
if !iter.Valid() {
|
if !iter.Valid() {
|
||||||
if v := st.Get(start); v != nil {
|
if v := st.Get(start); v != nil {
|
||||||
return cmn.KVPair{cp(start), cp(v)}, true
|
return cmn.KVPair{cp(start), cp(v)}, true
|
||||||
} else {
|
|
||||||
return kv, false
|
|
||||||
}
|
}
|
||||||
|
return kv, false
|
||||||
}
|
}
|
||||||
defer iter.Close()
|
defer iter.Close()
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ const (
|
||||||
defaultIAVLNumHistory = 1<<53 - 1 // DEPRECATED
|
defaultIAVLNumHistory = 1<<53 - 1 // DEPRECATED
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// load the iavl store
|
||||||
func LoadIAVLStore(db dbm.DB, id CommitID) (CommitStore, error) {
|
func LoadIAVLStore(db dbm.DB, id CommitID) (CommitStore, error) {
|
||||||
tree := iavl.NewVersionedTree(db, defaultIAVLCacheSize)
|
tree := iavl.NewVersionedTree(db, defaultIAVLCacheSize)
|
||||||
_, err := tree.LoadVersion(id.Version)
|
_, err := tree.LoadVersion(id.Version)
|
||||||
|
|
|
@ -80,7 +80,7 @@ func TestIAVLIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, treeData[expectedKey])
|
assert.EqualValues(t, value, treeData[expectedKey])
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ func TestIAVLIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, treeData[expectedKey])
|
assert.EqualValues(t, value, treeData[expectedKey])
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ func TestIAVLIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, treeData[expectedKey])
|
assert.EqualValues(t, value, treeData[expectedKey])
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ func TestIAVLIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, treeData[expectedKey])
|
assert.EqualValues(t, value, treeData[expectedKey])
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ func TestIAVLIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, treeData[expectedKey])
|
assert.EqualValues(t, value, treeData[expectedKey])
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ func TestIAVLIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, treeData[expectedKey])
|
assert.EqualValues(t, value, treeData[expectedKey])
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ func TestIAVLSubspaceIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, expectedKey)
|
assert.EqualValues(t, value, expectedKey)
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ func TestIAVLSubspaceIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, []byte("test4"))
|
assert.EqualValues(t, value, []byte("test4"))
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ func TestIAVLSubspaceIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, []byte("test4"))
|
assert.EqualValues(t, value, []byte("test4"))
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,7 @@ func TestIAVLReverseSubspaceIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, expectedKey)
|
assert.EqualValues(t, value, expectedKey)
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ func TestIAVLReverseSubspaceIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, []byte("test4"))
|
assert.EqualValues(t, value, []byte("test4"))
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
|
|
||||||
|
@ -253,7 +253,7 @@ func TestIAVLReverseSubspaceIterator(t *testing.T) {
|
||||||
key, value := iter.Key(), iter.Value()
|
key, value := iter.Key(), iter.Value()
|
||||||
assert.EqualValues(t, key, expectedKey)
|
assert.EqualValues(t, key, expectedKey)
|
||||||
assert.EqualValues(t, value, []byte("test4"))
|
assert.EqualValues(t, value, []byte("test4"))
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(expected), i)
|
assert.Equal(t, len(expected), i)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ type rootMultiStore struct {
|
||||||
var _ CommitMultiStore = (*rootMultiStore)(nil)
|
var _ CommitMultiStore = (*rootMultiStore)(nil)
|
||||||
var _ Queryable = (*rootMultiStore)(nil)
|
var _ Queryable = (*rootMultiStore)(nil)
|
||||||
|
|
||||||
|
// nolint
|
||||||
func NewCommitMultiStore(db dbm.DB) *rootMultiStore {
|
func NewCommitMultiStore(db dbm.DB) *rootMultiStore {
|
||||||
return &rootMultiStore{
|
return &rootMultiStore{
|
||||||
db: db,
|
db: db,
|
||||||
|
@ -267,7 +268,7 @@ func (rs *rootMultiStore) loadCommitStoreFromParams(id CommitID, params storePar
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *rootMultiStore) nameToKey(name string) StoreKey {
|
func (rs *rootMultiStore) nameToKey(name string) StoreKey {
|
||||||
for key, _ := range rs.storesParams {
|
for key := range rs.storesParams {
|
||||||
if key.Name() == name {
|
if key.Name() == name {
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,34 +128,34 @@ func TestMultiStoreQuery(t *testing.T) {
|
||||||
// Test bad path.
|
// Test bad path.
|
||||||
query := abci.RequestQuery{Path: "/key", Data: k, Height: ver}
|
query := abci.RequestQuery{Path: "/key", Data: k, Height: ver}
|
||||||
qres := multi.Query(query)
|
qres := multi.Query(query)
|
||||||
assert.Equal(t, uint32(sdk.CodeUnknownRequest), qres.Code)
|
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), sdk.ABCICodeType(qres.Code))
|
||||||
|
|
||||||
query.Path = "h897fy32890rf63296r92"
|
query.Path = "h897fy32890rf63296r92"
|
||||||
qres = multi.Query(query)
|
qres = multi.Query(query)
|
||||||
assert.Equal(t, uint32(sdk.CodeUnknownRequest), qres.Code)
|
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), sdk.ABCICodeType(qres.Code))
|
||||||
|
|
||||||
// Test invalid store name.
|
// Test invalid store name.
|
||||||
query.Path = "/garbage/key"
|
query.Path = "/garbage/key"
|
||||||
qres = multi.Query(query)
|
qres = multi.Query(query)
|
||||||
assert.Equal(t, uint32(sdk.CodeUnknownRequest), qres.Code)
|
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), sdk.ABCICodeType(qres.Code))
|
||||||
|
|
||||||
// Test valid query with data.
|
// Test valid query with data.
|
||||||
query.Path = "/store1/key"
|
query.Path = "/store1/key"
|
||||||
qres = multi.Query(query)
|
qres = multi.Query(query)
|
||||||
assert.Equal(t, uint32(sdk.CodeOK), qres.Code)
|
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOK), sdk.ABCICodeType(qres.Code))
|
||||||
assert.Equal(t, v, qres.Value)
|
assert.Equal(t, v, qres.Value)
|
||||||
|
|
||||||
// Test valid but empty query.
|
// Test valid but empty query.
|
||||||
query.Path = "/store2/key"
|
query.Path = "/store2/key"
|
||||||
query.Prove = true
|
query.Prove = true
|
||||||
qres = multi.Query(query)
|
qres = multi.Query(query)
|
||||||
assert.Equal(t, uint32(sdk.CodeOK), qres.Code)
|
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOK), sdk.ABCICodeType(qres.Code))
|
||||||
assert.Nil(t, qres.Value)
|
assert.Nil(t, qres.Value)
|
||||||
|
|
||||||
// Test store2 data.
|
// Test store2 data.
|
||||||
query.Data = k2
|
query.Data = k2
|
||||||
qres = multi.Query(query)
|
qres = multi.Query(query)
|
||||||
assert.Equal(t, uint32(sdk.CodeOK), qres.Code)
|
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOK), sdk.ABCICodeType(qres.Code))
|
||||||
assert.Equal(t, v2, qres.Value)
|
assert.Equal(t, v2, qres.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Import cosmos-sdk/types/store.go for convenience.
|
// Import cosmos-sdk/types/store.go for convenience.
|
||||||
|
// nolint
|
||||||
type Store = types.Store
|
type Store = types.Store
|
||||||
type Committer = types.Committer
|
type Committer = types.Committer
|
||||||
type CommitStore = types.CommitStore
|
type CommitStore = types.CommitStore
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getCmd(t *testing.T, command string) *exec.Cmd {
|
||||||
|
|
||||||
|
//split command into command and args
|
||||||
|
split := strings.Split(command, " ")
|
||||||
|
require.True(t, len(split) > 0, "no command provided")
|
||||||
|
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
if len(split) == 1 {
|
||||||
|
cmd = exec.Command(split[0])
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command(split[0], split[1:]...)
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the command, return standard output and error, try a few times if requested
|
||||||
|
func ExecuteT(t *testing.T, command string, trials int) (out string) {
|
||||||
|
cmd := getCmd(t, command)
|
||||||
|
bz, err := cmd.CombinedOutput()
|
||||||
|
if err != nil && trials > 1 {
|
||||||
|
fmt.Printf("trial %v, retrying: %v\n", trials, command)
|
||||||
|
time.Sleep(time.Second * 10)
|
||||||
|
return ExecuteT(t, command, trials-1)
|
||||||
|
}
|
||||||
|
require.NoError(t, err, string(bz))
|
||||||
|
out = strings.Trim(string(bz), "\n") //trim any new lines
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asynchronously execute the command, return standard output and error
|
||||||
|
func GoExecuteT(t *testing.T, command string) (cmd *exec.Cmd, pipeIn io.WriteCloser, pipeOut io.ReadCloser) {
|
||||||
|
cmd = getCmd(t, command)
|
||||||
|
pipeIn, err := cmd.StdinPipe()
|
||||||
|
require.NoError(t, err)
|
||||||
|
pipeOut, err = cmd.StdoutPipe()
|
||||||
|
require.NoError(t, err)
|
||||||
|
go cmd.Start()
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
return cmd, pipeIn, pipeOut
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// Codespacer is a simple struct to track reserved codespaces
|
||||||
|
type Codespacer struct {
|
||||||
|
reserved map[CodespaceType]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCodespacer generates a new Codespacer with the starting codespace
|
||||||
|
func NewCodespacer() *Codespacer {
|
||||||
|
return &Codespacer{
|
||||||
|
reserved: make(map[CodespaceType]bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterNext reserves and returns the next available codespace, starting from a default, and panics if the maximum codespace is reached
|
||||||
|
func (c *Codespacer) RegisterNext(codespace CodespaceType) CodespaceType {
|
||||||
|
for {
|
||||||
|
if !c.reserved[codespace] {
|
||||||
|
c.reserved[codespace] = true
|
||||||
|
return codespace
|
||||||
|
}
|
||||||
|
codespace++
|
||||||
|
if codespace == MaximumCodespace {
|
||||||
|
panic("Maximum codespace reached!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterOrPanic reserved a codespace or panics if it is unavailable
|
||||||
|
func (c *Codespacer) RegisterOrPanic(codespace CodespaceType) {
|
||||||
|
if c.reserved[codespace] {
|
||||||
|
panic("Cannot register codespace, already reserved")
|
||||||
|
}
|
||||||
|
c.reserved[codespace] = true
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRegisterNext(t *testing.T) {
|
||||||
|
codespacer := NewCodespacer()
|
||||||
|
// unregistered, allow
|
||||||
|
code1 := codespacer.RegisterNext(CodespaceType(2))
|
||||||
|
require.Equal(t, code1, CodespaceType(2))
|
||||||
|
// registered, pick next
|
||||||
|
code2 := codespacer.RegisterNext(CodespaceType(2))
|
||||||
|
require.Equal(t, code2, CodespaceType(3))
|
||||||
|
// pick next
|
||||||
|
code3 := codespacer.RegisterNext(CodespaceType(2))
|
||||||
|
require.Equal(t, code3, CodespaceType(4))
|
||||||
|
// skip 1
|
||||||
|
code4 := codespacer.RegisterNext(CodespaceType(6))
|
||||||
|
require.Equal(t, code4, CodespaceType(6))
|
||||||
|
code5 := codespacer.RegisterNext(CodespaceType(2))
|
||||||
|
require.Equal(t, code5, CodespaceType(5))
|
||||||
|
code6 := codespacer.RegisterNext(CodespaceType(2))
|
||||||
|
require.Equal(t, code6, CodespaceType(7))
|
||||||
|
// panic on maximum
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
require.NotNil(t, r, "Did not panic on maximum codespace")
|
||||||
|
}()
|
||||||
|
codespacer.RegisterNext(MaximumCodespace - 1)
|
||||||
|
codespacer.RegisterNext(MaximumCodespace - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterOrPanic(t *testing.T) {
|
||||||
|
codespacer := NewCodespacer()
|
||||||
|
// unregistered, allow
|
||||||
|
code1 := codespacer.RegisterNext(CodespaceType(2))
|
||||||
|
require.Equal(t, code1, CodespaceType(2))
|
||||||
|
// panic on duplicate
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
require.NotNil(t, r, "Did not panic on duplicate codespace")
|
||||||
|
}()
|
||||||
|
codespacer.RegisterOrPanic(CodespaceType(2))
|
||||||
|
}
|
189
types/errors.go
189
types/errors.go
|
@ -2,25 +2,44 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ABCI Response Code
|
// ABCICodeType - combined codetype / codespace
|
||||||
type CodeType uint32
|
type ABCICodeType uint32
|
||||||
|
|
||||||
// is everything okay?
|
// CodeType - code identifier within codespace
|
||||||
func (code CodeType) IsOK() bool {
|
type CodeType uint16
|
||||||
if code == CodeOK {
|
|
||||||
|
// CodespaceType - codespace identifier
|
||||||
|
type CodespaceType uint16
|
||||||
|
|
||||||
|
// IsOK - is everything okay?
|
||||||
|
func (code ABCICodeType) IsOK() bool {
|
||||||
|
if code == ABCICodeOK {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ABCI Response Codes
|
// get the abci code from the local code and codespace
|
||||||
// Base SDK reserves 0 - 99.
|
func ToABCICode(space CodespaceType, code CodeType) ABCICodeType {
|
||||||
|
// TODO: Make Tendermint more aware of codespaces.
|
||||||
|
if space == CodespaceRoot && code == CodeOK {
|
||||||
|
return ABCICodeOK
|
||||||
|
}
|
||||||
|
return ABCICodeType((uint32(space) << 16) | uint32(code))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SDK error codes
|
||||||
const (
|
const (
|
||||||
|
// ABCI error codes
|
||||||
|
ABCICodeOK ABCICodeType = 0
|
||||||
|
|
||||||
|
// Base error codes
|
||||||
CodeOK CodeType = 0
|
CodeOK CodeType = 0
|
||||||
CodeInternal CodeType = 1
|
CodeInternal CodeType = 1
|
||||||
CodeTxDecode CodeType = 2
|
CodeTxDecode CodeType = 2
|
||||||
|
@ -34,7 +53,14 @@ const (
|
||||||
CodeInsufficientCoins CodeType = 10
|
CodeInsufficientCoins CodeType = 10
|
||||||
CodeInvalidCoins CodeType = 11
|
CodeInvalidCoins CodeType = 11
|
||||||
|
|
||||||
CodeGenesisParse CodeType = 0xdead // TODO: remove ? // why remove?
|
// CodespaceRoot is a codespace for error codes in this file only.
|
||||||
|
// Notice that 0 is an "unset" codespace, which can be overridden with
|
||||||
|
// Error.WithDefaultCodespace().
|
||||||
|
CodespaceUndefined CodespaceType = 0
|
||||||
|
CodespaceRoot CodespaceType = 1
|
||||||
|
|
||||||
|
// Maximum reservable codespace (2^16 - 1)
|
||||||
|
MaximumCodespace CodespaceType = 65535
|
||||||
)
|
)
|
||||||
|
|
||||||
// NOTE: Don't stringer this, we'll put better messages in later.
|
// NOTE: Don't stringer this, we'll put better messages in later.
|
||||||
|
@ -44,8 +70,6 @@ func CodeToDefaultMsg(code CodeType) string {
|
||||||
return "Internal error"
|
return "Internal error"
|
||||||
case CodeTxDecode:
|
case CodeTxDecode:
|
||||||
return "Tx parse error"
|
return "Tx parse error"
|
||||||
case CodeGenesisParse:
|
|
||||||
return "Genesis parse error"
|
|
||||||
case CodeInvalidSequence:
|
case CodeInvalidSequence:
|
||||||
return "Invalid sequence"
|
return "Invalid sequence"
|
||||||
case CodeUnauthorized:
|
case CodeUnauthorized:
|
||||||
|
@ -75,40 +99,37 @@ func CodeToDefaultMsg(code CodeType) string {
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
func ErrInternal(msg string) Error {
|
func ErrInternal(msg string) Error {
|
||||||
return newError(CodeInternal, msg)
|
return newErrorWithRootCodespace(CodeInternal, msg)
|
||||||
}
|
}
|
||||||
func ErrTxDecode(msg string) Error {
|
func ErrTxDecode(msg string) Error {
|
||||||
return newError(CodeTxDecode, msg)
|
return newErrorWithRootCodespace(CodeTxDecode, msg)
|
||||||
}
|
|
||||||
func ErrGenesisParse(msg string) Error {
|
|
||||||
return newError(CodeGenesisParse, msg)
|
|
||||||
}
|
}
|
||||||
func ErrInvalidSequence(msg string) Error {
|
func ErrInvalidSequence(msg string) Error {
|
||||||
return newError(CodeInvalidSequence, msg)
|
return newErrorWithRootCodespace(CodeInvalidSequence, msg)
|
||||||
}
|
}
|
||||||
func ErrUnauthorized(msg string) Error {
|
func ErrUnauthorized(msg string) Error {
|
||||||
return newError(CodeUnauthorized, msg)
|
return newErrorWithRootCodespace(CodeUnauthorized, msg)
|
||||||
}
|
}
|
||||||
func ErrInsufficientFunds(msg string) Error {
|
func ErrInsufficientFunds(msg string) Error {
|
||||||
return newError(CodeInsufficientFunds, msg)
|
return newErrorWithRootCodespace(CodeInsufficientFunds, msg)
|
||||||
}
|
}
|
||||||
func ErrUnknownRequest(msg string) Error {
|
func ErrUnknownRequest(msg string) Error {
|
||||||
return newError(CodeUnknownRequest, msg)
|
return newErrorWithRootCodespace(CodeUnknownRequest, msg)
|
||||||
}
|
}
|
||||||
func ErrInvalidAddress(msg string) Error {
|
func ErrInvalidAddress(msg string) Error {
|
||||||
return newError(CodeInvalidAddress, msg)
|
return newErrorWithRootCodespace(CodeInvalidAddress, msg)
|
||||||
}
|
}
|
||||||
func ErrUnknownAddress(msg string) Error {
|
func ErrUnknownAddress(msg string) Error {
|
||||||
return newError(CodeUnknownAddress, msg)
|
return newErrorWithRootCodespace(CodeUnknownAddress, msg)
|
||||||
}
|
}
|
||||||
func ErrInvalidPubKey(msg string) Error {
|
func ErrInvalidPubKey(msg string) Error {
|
||||||
return newError(CodeInvalidPubKey, msg)
|
return newErrorWithRootCodespace(CodeInvalidPubKey, msg)
|
||||||
}
|
}
|
||||||
func ErrInsufficientCoins(msg string) Error {
|
func ErrInsufficientCoins(msg string) Error {
|
||||||
return newError(CodeInsufficientCoins, msg)
|
return newErrorWithRootCodespace(CodeInsufficientCoins, msg)
|
||||||
}
|
}
|
||||||
func ErrInvalidCoins(msg string) Error {
|
func ErrInvalidCoins(msg string) Error {
|
||||||
return newError(CodeInvalidCoins, msg)
|
return newErrorWithRootCodespace(CodeInvalidCoins, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
@ -117,104 +138,98 @@ func ErrInvalidCoins(msg string) Error {
|
||||||
// sdk Error type
|
// sdk Error type
|
||||||
type Error interface {
|
type Error interface {
|
||||||
Error() string
|
Error() string
|
||||||
ABCICode() CodeType
|
Code() CodeType
|
||||||
|
Codespace() CodespaceType
|
||||||
ABCILog() string
|
ABCILog() string
|
||||||
|
ABCICode() ABCICodeType
|
||||||
|
WithDefaultCodespace(codespace CodespaceType) Error
|
||||||
Trace(msg string) Error
|
Trace(msg string) Error
|
||||||
TraceCause(cause error, msg string) Error
|
T() interface{}
|
||||||
Cause() error
|
|
||||||
Result() Result
|
Result() Result
|
||||||
QueryResult() abci.ResponseQuery
|
QueryResult() abci.ResponseQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewError(code CodeType, msg string) Error {
|
// NewError - create an error
|
||||||
return newError(code, msg)
|
func NewError(codespace CodespaceType, code CodeType, msg string) Error {
|
||||||
|
return newError(codespace, code, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
type traceItem struct {
|
func newErrorWithRootCodespace(code CodeType, msg string) *sdkError {
|
||||||
msg string
|
return newError(CodespaceRoot, code, msg)
|
||||||
filename string
|
|
||||||
lineno int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ti traceItem) String() string {
|
func newError(codespace CodespaceType, code CodeType, msg string) *sdkError {
|
||||||
return fmt.Sprintf("%v:%v %v", ti.filename, ti.lineno, ti.msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
type sdkError struct {
|
|
||||||
code CodeType
|
|
||||||
msg string
|
|
||||||
cause error
|
|
||||||
traces []traceItem
|
|
||||||
}
|
|
||||||
|
|
||||||
func newError(code CodeType, msg string) *sdkError {
|
|
||||||
// TODO capture stacktrace if ENV is set.
|
|
||||||
if msg == "" {
|
if msg == "" {
|
||||||
msg = CodeToDefaultMsg(code)
|
msg = CodeToDefaultMsg(code)
|
||||||
}
|
}
|
||||||
return &sdkError{
|
return &sdkError{
|
||||||
|
codespace: codespace,
|
||||||
code: code,
|
code: code,
|
||||||
msg: msg,
|
err: cmn.NewErrorWithT(code, msg),
|
||||||
cause: nil,
|
|
||||||
traces: nil,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sdkError struct {
|
||||||
|
codespace CodespaceType
|
||||||
|
code CodeType
|
||||||
|
err cmn.Error
|
||||||
|
}
|
||||||
|
|
||||||
// Implements ABCIError.
|
// Implements ABCIError.
|
||||||
func (err *sdkError) Error() string {
|
func (err *sdkError) Error() string {
|
||||||
return fmt.Sprintf("Error{%d:%s,%v,%v}", err.code, err.msg, err.cause, len(err.traces))
|
return fmt.Sprintf("Error{%d:%d,%#v}", err.codespace, err.code, err.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements ABCIError.
|
// Implements ABCIError.
|
||||||
func (err *sdkError) ABCICode() CodeType {
|
func (err *sdkError) ABCICode() ABCICodeType {
|
||||||
|
return ToABCICode(err.codespace, err.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Error.
|
||||||
|
func (err *sdkError) Codespace() CodespaceType {
|
||||||
|
return err.codespace
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Error.
|
||||||
|
func (err *sdkError) Code() CodeType {
|
||||||
return err.code
|
return err.code
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements ABCIError.
|
// Implements ABCIError.
|
||||||
func (err *sdkError) ABCILog() string {
|
func (err *sdkError) ABCILog() string {
|
||||||
traceLog := ""
|
return fmt.Sprintf(`=== ABCI Log ===
|
||||||
for _, ti := range err.traces {
|
Codespace: %v
|
||||||
traceLog += ti.String() + "\n"
|
Code: %v
|
||||||
}
|
ABCICode: %v
|
||||||
return fmt.Sprintf("msg: %v\ntrace:\n%v",
|
Error: %#v
|
||||||
err.msg,
|
=== /ABCI Log ===
|
||||||
traceLog,
|
`, err.codespace, err.code, err.ABCICode(), err.err)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tracing information with msg.
|
// Add tracing information with msg.
|
||||||
func (err *sdkError) Trace(msg string) Error {
|
func (err *sdkError) Trace(msg string) Error {
|
||||||
return err.doTrace(msg, 2)
|
return &sdkError{
|
||||||
|
codespace: err.codespace,
|
||||||
|
code: err.code,
|
||||||
|
err: err.err.Trace(msg),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tracing information with cause and msg.
|
// Implements Error.
|
||||||
func (err *sdkError) TraceCause(cause error, msg string) Error {
|
func (err *sdkError) WithDefaultCodespace(cs CodespaceType) Error {
|
||||||
err.cause = cause
|
codespace := err.codespace
|
||||||
return err.doTrace(msg, 2)
|
if codespace == CodespaceUndefined {
|
||||||
|
codespace = cs
|
||||||
|
}
|
||||||
|
return &sdkError{
|
||||||
|
codespace: codespace,
|
||||||
|
code: err.code,
|
||||||
|
err: err.err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *sdkError) doTrace(msg string, n int) Error {
|
func (err *sdkError) T() interface{} {
|
||||||
_, fn, line, ok := runtime.Caller(n)
|
return err.err.T()
|
||||||
if !ok {
|
|
||||||
if fn == "" {
|
|
||||||
fn = "<unknown>"
|
|
||||||
}
|
|
||||||
if line <= 0 {
|
|
||||||
line = -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Include file & line number & msg.
|
|
||||||
// Do not include the whole stack trace.
|
|
||||||
err.traces = append(err.traces, traceItem{
|
|
||||||
filename: fn,
|
|
||||||
lineno: line,
|
|
||||||
msg: msg,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err *sdkError) Cause() error {
|
|
||||||
return err.cause
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *sdkError) Result() Result {
|
func (err *sdkError) Result() Result {
|
||||||
|
|
|
@ -16,7 +16,6 @@ var codeTypes = []CodeType{
|
||||||
CodeUnknownRequest,
|
CodeUnknownRequest,
|
||||||
CodeUnknownAddress,
|
CodeUnknownAddress,
|
||||||
CodeInvalidPubKey,
|
CodeInvalidPubKey,
|
||||||
CodeGenesisParse,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type errFn func(msg string) Error
|
type errFn func(msg string) Error
|
||||||
|
@ -30,14 +29,12 @@ var errFns = []errFn{
|
||||||
ErrUnknownRequest,
|
ErrUnknownRequest,
|
||||||
ErrUnknownAddress,
|
ErrUnknownAddress,
|
||||||
ErrInvalidPubKey,
|
ErrInvalidPubKey,
|
||||||
ErrGenesisParse,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCodeType(t *testing.T) {
|
func TestCodeType(t *testing.T) {
|
||||||
assert.True(t, CodeOK.IsOK())
|
assert.True(t, ABCICodeOK.IsOK())
|
||||||
|
|
||||||
for _, c := range codeTypes {
|
for _, c := range codeTypes {
|
||||||
assert.False(t, c.IsOK())
|
|
||||||
msg := CodeToDefaultMsg(c)
|
msg := CodeToDefaultMsg(c)
|
||||||
assert.False(t, strings.HasPrefix(msg, "Unknown code"))
|
assert.False(t, strings.HasPrefix(msg, "Unknown code"))
|
||||||
}
|
}
|
||||||
|
@ -47,7 +44,7 @@ func TestErrFn(t *testing.T) {
|
||||||
for i, errFn := range errFns {
|
for i, errFn := range errFns {
|
||||||
err := errFn("")
|
err := errFn("")
|
||||||
codeType := codeTypes[i]
|
codeType := codeTypes[i]
|
||||||
assert.Equal(t, err.ABCICode(), codeType)
|
assert.Equal(t, err.Code(), codeType)
|
||||||
assert.Equal(t, err.Result().Code, codeType)
|
assert.Equal(t, err.Result().Code, ToABCICode(CodespaceRoot, codeType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,23 +97,23 @@ func NewRatFromDecimal(decimalStr string) (f Rat, err Error) {
|
||||||
switch len(str) {
|
switch len(str) {
|
||||||
case 1:
|
case 1:
|
||||||
if len(str[0]) == 0 {
|
if len(str[0]) == 0 {
|
||||||
return f, NewError(CodeUnknownRequest, "not a decimal string")
|
return f, ErrUnknownRequest("not a decimal string")
|
||||||
}
|
}
|
||||||
numStr = str[0]
|
numStr = str[0]
|
||||||
case 2:
|
case 2:
|
||||||
if len(str[0]) == 0 || len(str[1]) == 0 {
|
if len(str[0]) == 0 || len(str[1]) == 0 {
|
||||||
return f, NewError(CodeUnknownRequest, "not a decimal string")
|
return f, ErrUnknownRequest("not a decimal string")
|
||||||
}
|
}
|
||||||
numStr = str[0] + str[1]
|
numStr = str[0] + str[1]
|
||||||
len := int64(len(str[1]))
|
len := int64(len(str[1]))
|
||||||
denom = new(big.Int).Exp(big.NewInt(10), big.NewInt(len), nil).Int64()
|
denom = new(big.Int).Exp(big.NewInt(10), big.NewInt(len), nil).Int64()
|
||||||
default:
|
default:
|
||||||
return f, NewError(CodeUnknownRequest, "not a decimal string")
|
return f, ErrUnknownRequest("not a decimal string")
|
||||||
}
|
}
|
||||||
|
|
||||||
num, errConv := strconv.Atoi(numStr)
|
num, errConv := strconv.Atoi(numStr)
|
||||||
if errConv != nil {
|
if errConv != nil {
|
||||||
return f, NewError(CodeUnknownRequest, errConv.Error())
|
return f, ErrUnknownRequest(errConv.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if neg {
|
if neg {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
type Result struct {
|
type Result struct {
|
||||||
|
|
||||||
// Code is the response code, is stored back on the chain.
|
// Code is the response code, is stored back on the chain.
|
||||||
Code CodeType
|
Code ABCICodeType
|
||||||
|
|
||||||
// Data is any data returned from the app.
|
// Data is any data returned from the app.
|
||||||
Data []byte
|
Data []byte
|
||||||
|
@ -20,7 +20,7 @@ type Result struct {
|
||||||
// GasWanted is the maximum units of work we allow this tx to perform.
|
// GasWanted is the maximum units of work we allow this tx to perform.
|
||||||
GasWanted int64
|
GasWanted int64
|
||||||
|
|
||||||
// GasUsed is the amount of gas actually consumed. NOTE: not used.
|
// GasUsed is the amount of gas actually consumed. NOTE: unimplemented
|
||||||
GasUsed int64
|
GasUsed int64
|
||||||
|
|
||||||
// Tx fee amount and denom.
|
// Tx fee amount and denom.
|
||||||
|
|
|
@ -25,15 +25,13 @@ func getVersion() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CMD
|
// CMD
|
||||||
|
|
||||||
func printVersion(cmd *cobra.Command, args []string) {
|
func printVersion(cmd *cobra.Command, args []string) {
|
||||||
v := getVersion()
|
v := getVersion()
|
||||||
fmt.Println(v)
|
fmt.Println(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// REST
|
// version REST handler endpoint
|
||||||
|
func RequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
func VersionRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
v := getVersion()
|
v := getVersion()
|
||||||
w.Write([]byte(v))
|
w.Write([]byte(v))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// amino codec to marshal/unmarshal
|
||||||
type Codec = amino.Codec
|
type Codec = amino.Codec
|
||||||
|
|
||||||
func NewCodec() *Codec {
|
func NewCodec() *Codec {
|
||||||
|
@ -15,10 +16,12 @@ func NewCodec() *Codec {
|
||||||
return cdc
|
return cdc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register the go-crypto to the codec
|
||||||
func RegisterCrypto(cdc *Codec) {
|
func RegisterCrypto(cdc *Codec) {
|
||||||
crypto.RegisterAmino(cdc)
|
crypto.RegisterAmino(cdc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// attempt to make some pretty json
|
||||||
func MarshalJSONIndent(cdc *Codec, obj interface{}) ([]byte, error) {
|
func MarshalJSONIndent(cdc *Codec, obj interface{}) ([]byte, error) {
|
||||||
bz, err := cdc.MarshalJSON(obj)
|
bz, err := cdc.MarshalJSON(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -40,7 +40,7 @@ func privAndAddr() (crypto.PrivKey, sdk.Address) {
|
||||||
func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx) {
|
func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx) {
|
||||||
_, result, abort := anteHandler(ctx, tx)
|
_, result, abort := anteHandler(ctx, tx)
|
||||||
assert.False(t, abort)
|
assert.False(t, abort)
|
||||||
assert.Equal(t, sdk.CodeOK, result.Code)
|
assert.Equal(t, sdk.ABCICodeOK, result.Code)
|
||||||
assert.True(t, result.IsOK())
|
assert.True(t, result.IsOK())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx
|
||||||
func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, code sdk.CodeType) {
|
func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, code sdk.CodeType) {
|
||||||
_, result, abort := anteHandler(ctx, tx)
|
_, result, abort := anteHandler(ctx, tx)
|
||||||
assert.True(t, abort)
|
assert.True(t, abort)
|
||||||
assert.Equal(t, code, result.Code)
|
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, code), result.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestTx(ctx sdk.Context, msg sdk.Msg, privs []crypto.PrivKey, seqs []int64, fee sdk.StdFee) sdk.Tx {
|
func newTestTx(ctx sdk.Context, msg sdk.Msg, privs []crypto.PrivKey, seqs []int64, fee sdk.StdFee) sdk.Tx {
|
||||||
|
|
|
@ -17,6 +17,7 @@ func GetAccountCmdDefault(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||||
return GetAccountCmd(storeName, cdc, GetAccountDecoder(cdc))
|
return GetAccountCmd(storeName, cdc, GetAccountDecoder(cdc))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get account decoder for auth.DefaultAccount
|
||||||
func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder {
|
func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder {
|
||||||
return func(accBytes []byte) (acct sdk.Account, err error) {
|
return func(accBytes []byte) (acct sdk.Account, err error) {
|
||||||
// acct := new(auth.BaseAccount)
|
// acct := new(auth.BaseAccount)
|
||||||
|
|
|
@ -33,10 +33,12 @@ const (
|
||||||
contextKeySigners contextKey = iota
|
contextKeySigners contextKey = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// add the signers to the context
|
||||||
func WithSigners(ctx types.Context, accounts []types.Account) types.Context {
|
func WithSigners(ctx types.Context, accounts []types.Account) types.Context {
|
||||||
return ctx.WithValue(contextKeySigners, accounts)
|
return ctx.WithValue(contextKeySigners, accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the signers from the context
|
||||||
func GetSigners(ctx types.Context) []types.Account {
|
func GetSigners(ctx types.Context) []types.Account {
|
||||||
v := ctx.Value(contextKeySigners)
|
v := ctx.Value(contextKeySigners)
|
||||||
if v == nil {
|
if v == nil {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ sdk.AccountMapper = (*accountMapper)(nil)
|
var _ sdk.AccountMapper = (*accountMapper)(nil)
|
||||||
var _ sdk.AccountMapper = (*sealedAccountMapper)(nil)
|
|
||||||
|
|
||||||
// Implements sdk.AccountMapper.
|
// Implements sdk.AccountMapper.
|
||||||
// This AccountMapper encodes/decodes accounts using the
|
// This AccountMapper encodes/decodes accounts using the
|
||||||
|
@ -28,6 +27,7 @@ type accountMapper struct {
|
||||||
|
|
||||||
// NewAccountMapper returns a new sdk.AccountMapper that
|
// NewAccountMapper returns a new sdk.AccountMapper that
|
||||||
// uses go-amino to (binary) encode and decode concrete sdk.Accounts.
|
// uses go-amino to (binary) encode and decode concrete sdk.Accounts.
|
||||||
|
// nolint
|
||||||
func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto sdk.Account) accountMapper {
|
func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto sdk.Account) accountMapper {
|
||||||
return accountMapper{
|
return accountMapper{
|
||||||
key: key,
|
key: key,
|
||||||
|
@ -36,21 +36,6 @@ func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto sdk.Account) acco
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the go-amino codec. You may need to register interfaces
|
|
||||||
// and concrete types here, if your app's sdk.Account
|
|
||||||
// implementation includes interface fields.
|
|
||||||
// NOTE: It is not secure to expose the codec, so check out
|
|
||||||
// .Seal().
|
|
||||||
func (am accountMapper) WireCodec() *wire.Codec {
|
|
||||||
return am.cdc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a "sealed" accountMapper.
|
|
||||||
// The codec is not accessible from a sealedAccountMapper.
|
|
||||||
func (am accountMapper) Seal() sealedAccountMapper {
|
|
||||||
return sealedAccountMapper{am}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.AccountMapper.
|
// Implements sdk.AccountMapper.
|
||||||
func (am accountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) sdk.Account {
|
func (am accountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) sdk.Account {
|
||||||
acc := am.clonePrototype()
|
acc := am.clonePrototype()
|
||||||
|
@ -77,19 +62,6 @@ func (am accountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) {
|
||||||
store.Set(addr, bz)
|
store.Set(addr, bz)
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
|
||||||
// sealedAccountMapper
|
|
||||||
|
|
||||||
type sealedAccountMapper struct {
|
|
||||||
accountMapper
|
|
||||||
}
|
|
||||||
|
|
||||||
// There's no way for external modules to mutate the
|
|
||||||
// sam.accountMapper.cdc from here, even with reflection.
|
|
||||||
func (sam sealedAccountMapper) WireCodec() *wire.Codec {
|
|
||||||
panic("accountMapper is sealed")
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
// misc.
|
// misc.
|
||||||
|
|
||||||
|
@ -107,7 +79,8 @@ func (am accountMapper) clonePrototype() sdk.Account {
|
||||||
panic(fmt.Sprintf("accountMapper requires a proto sdk.Account, but %v doesn't implement sdk.Account", protoRt))
|
panic(fmt.Sprintf("accountMapper requires a proto sdk.Account, but %v doesn't implement sdk.Account", protoRt))
|
||||||
}
|
}
|
||||||
return clone
|
return clone
|
||||||
} else {
|
}
|
||||||
|
|
||||||
protoRv := reflect.New(protoRt).Elem()
|
protoRv := reflect.New(protoRt).Elem()
|
||||||
clone, ok := protoRv.Interface().(sdk.Account)
|
clone, ok := protoRv.Interface().(sdk.Account)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -115,7 +88,6 @@ func (am accountMapper) clonePrototype() sdk.Account {
|
||||||
}
|
}
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (am accountMapper) encodeAccount(acc sdk.Account) []byte {
|
func (am accountMapper) encodeAccount(acc sdk.Account) []byte {
|
||||||
bz, err := am.cdc.MarshalBinaryBare(acc)
|
bz, err := am.cdc.MarshalBinaryBare(acc)
|
||||||
|
|
|
@ -57,17 +57,3 @@ func TestAccountMapperGetSet(t *testing.T) {
|
||||||
assert.NotNil(t, acc)
|
assert.NotNil(t, acc)
|
||||||
assert.Equal(t, newSequence, acc.GetSequence())
|
assert.Equal(t, newSequence, acc.GetSequence())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccountMapperSealed(t *testing.T) {
|
|
||||||
_, capKey := setupMultiStore()
|
|
||||||
cdc := wire.NewCodec()
|
|
||||||
RegisterBaseAccount(cdc)
|
|
||||||
|
|
||||||
// normal mapper exposes the wire codec
|
|
||||||
mapper := NewAccountMapper(cdc, capKey, &BaseAccount{})
|
|
||||||
assert.NotNil(t, mapper.WireCodec())
|
|
||||||
|
|
||||||
// seal mapper, should panic when we try to get the codec
|
|
||||||
mapperSealed := mapper.Seal()
|
|
||||||
assert.Panics(t, func() { mapperSealed.WireCodec() })
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,16 +10,19 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
auth "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||||
)
|
)
|
||||||
|
|
||||||
type commander struct {
|
// register REST routes
|
||||||
storeName string
|
func RegisterRoutes(r *mux.Router, cdc *wire.Codec, storeName string) {
|
||||||
cdc *wire.Codec
|
r.HandleFunc(
|
||||||
decoder sdk.AccountDecoder
|
"/accounts/{address}",
|
||||||
|
QueryAccountRequestHandler(storeName, cdc, auth.GetAccountDecoder(cdc)),
|
||||||
|
).Methods("GET")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// query accountREST Handler
|
||||||
func QueryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder sdk.AccountDecoder) func(http.ResponseWriter, *http.Request) {
|
func QueryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder sdk.AccountDecoder) func(http.ResponseWriter, *http.Request) {
|
||||||
c := commander{storeName, cdc, decoder}
|
|
||||||
ctx := context.NewCoreContextFromViper()
|
ctx := context.NewCoreContextFromViper()
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
@ -33,7 +36,7 @@ func QueryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder sdk.A
|
||||||
}
|
}
|
||||||
key := sdk.Address(bz)
|
key := sdk.Address(bz)
|
||||||
|
|
||||||
res, err := ctx.Query(key, c.storeName)
|
res, err := ctx.Query(key, storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error())))
|
w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error())))
|
||||||
|
@ -47,7 +50,7 @@ func QueryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder sdk.A
|
||||||
}
|
}
|
||||||
|
|
||||||
// decode the value
|
// decode the value
|
||||||
account, err := c.decoder(res)
|
account, err := decoder(res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(fmt.Sprintf("Could't parse query result. Result: %s. Error: %s", res, err.Error())))
|
w.Write([]byte(fmt.Sprintf("Could't parse query result. Result: %s. Error: %s", res, err.Error())))
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue