merge from develop
This commit is contained in:
commit
8ff316c99e
|
@ -27,11 +27,17 @@ jobs:
|
|||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make get_vendor_deps
|
||||
- run:
|
||||
name: linter
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
go get -u github.com/tendermint/lint/golint
|
||||
go get -u github.com/alecthomas/gometalinter
|
||||
- run:
|
||||
name: binaries
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make build
|
||||
make install
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
|
@ -46,6 +52,22 @@ jobs:
|
|||
paths:
|
||||
- /go/src/github.com/cosmos/cosmos-sdk
|
||||
|
||||
lint:
|
||||
<<: *defaults
|
||||
parallelism: 4
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v1-pkg-cache
|
||||
- restore_cache:
|
||||
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: Lint source
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
gometalinter --disable-all --enable='golint' --vendor ./...
|
||||
|
||||
test_cover:
|
||||
<<: *defaults
|
||||
parallelism: 4
|
||||
|
@ -59,7 +81,9 @@ jobs:
|
|||
- run:
|
||||
name: Run tests
|
||||
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")
|
||||
|
||||
go test -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg"
|
||||
|
@ -94,10 +118,12 @@ workflows:
|
|||
test-suite:
|
||||
jobs:
|
||||
- setup_dependencies
|
||||
- lint:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- test_cover:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- upload_coverage:
|
||||
requires:
|
||||
- test_cover
|
||||
|
||||
|
|
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -7,16 +7,26 @@ FEATURES:
|
|||
* Add CacheContext
|
||||
* Add auto sequencing to client
|
||||
* Add FeeHandler to ante handler
|
||||
* Gaia stake commands include, DeclareCandidacy, EditCandidacy, Delegate, Unbond
|
||||
* MountStoreWithDB without providing a custom store works.
|
||||
* Repo is now lint compliant / GoMetaLinter with tendermint-lint integrated into CI
|
||||
* Better key output, pubkey go-amino hex bytes now output by default
|
||||
|
||||
BREAKING CHANGES
|
||||
|
||||
* 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
|
||||
* [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
|
||||
|
||||
* MountStoreWithDB without providing a custom store works.
|
||||
* Gaia now uses stake, ported from github.com/cosmos/gaia
|
||||
|
||||
## 0.14.1 (April 9, 2018)
|
||||
|
||||
|
|
|
@ -342,7 +342,7 @@
|
|||
"types/priv_validator",
|
||||
"version"
|
||||
]
|
||||
revision = "d0beaba7e8a5652506a34b5fab299cc2dc274c02"
|
||||
revision = "a2930cd7233f04f5a651020669289296545e70dc"
|
||||
version = "v0.19.0"
|
||||
|
||||
[[projects]]
|
||||
|
@ -384,6 +384,7 @@
|
|||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
"http/httpguts",
|
||||
"http2",
|
||||
"http2/hpack",
|
||||
"idna",
|
||||
|
@ -391,13 +392,13 @@
|
|||
"lex/httplex",
|
||||
"trace"
|
||||
]
|
||||
revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
|
||||
revision = "8d16fa6dc9a85c1cd3ed24ad08ff21cf94f10888"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
|
||||
revision = "b126b21c05a91c856b027c16779c12e3bf236954"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/text"
|
||||
|
@ -424,7 +425,7 @@
|
|||
branch = "master"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
revision = "51d0944304c3cbce4afe9e5247e21100037bff78"
|
||||
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
|
||||
|
||||
[[projects]]
|
||||
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)
|
||||
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: get_tools get_vendor_deps build test_cover
|
||||
ci: get_tools get_vendor_deps install test_cover
|
||||
|
||||
########################################
|
||||
### Build
|
||||
|
@ -15,11 +15,11 @@ ci: get_tools get_vendor_deps build test_cover
|
|||
# This can be unified later, here for easy demos
|
||||
build:
|
||||
ifeq ($(OS),Windows_NT)
|
||||
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaiad
|
||||
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaiacli
|
||||
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad
|
||||
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli
|
||||
else
|
||||
go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaiad
|
||||
go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaiacli
|
||||
go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaia/cmd/gaiad
|
||||
go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaia/cmd/gaiacli
|
||||
endif
|
||||
|
||||
build_examples:
|
||||
|
@ -36,8 +36,8 @@ else
|
|||
endif
|
||||
|
||||
install:
|
||||
go install $(BUILD_FLAGS) ./cmd/gaiad
|
||||
go install $(BUILD_FLAGS) ./cmd/gaiacli
|
||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad
|
||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli
|
||||
|
||||
install_examples:
|
||||
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecoind
|
||||
|
|
|
@ -24,11 +24,12 @@ var dbHeaderKey = []byte("header")
|
|||
// The ABCI application
|
||||
type BaseApp struct {
|
||||
// initialized on creation
|
||||
Logger log.Logger
|
||||
name string // application name from abci.Info
|
||||
db dbm.DB // common DB backend
|
||||
cms sdk.CommitMultiStore // Main (uncached) state
|
||||
router Router // handle any kind of message
|
||||
Logger log.Logger
|
||||
name string // application name from abci.Info
|
||||
db dbm.DB // common DB backend
|
||||
cms sdk.CommitMultiStore // Main (uncached) state
|
||||
router Router // handle any kind of message
|
||||
codespacer *sdk.Codespacer // handle module codespacing
|
||||
|
||||
// must be set
|
||||
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
|
||||
|
@ -56,13 +57,18 @@ var _ abci.Application = (*BaseApp)(nil)
|
|||
// Create and name new BaseApp
|
||||
// NOTE: The db is used to store the version number for now.
|
||||
func NewBaseApp(name string, logger log.Logger, db dbm.DB) *BaseApp {
|
||||
return &BaseApp{
|
||||
Logger: logger,
|
||||
name: name,
|
||||
db: db,
|
||||
cms: store.NewCommitMultiStore(db),
|
||||
router: NewRouter(),
|
||||
app := &BaseApp{
|
||||
Logger: logger,
|
||||
name: name,
|
||||
db: db,
|
||||
cms: store.NewCommitMultiStore(db),
|
||||
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
|
||||
|
@ -70,6 +76,11 @@ func (app *BaseApp) Name() string {
|
|||
return app.name
|
||||
}
|
||||
|
||||
// Register the next available codespace through the baseapp's codespacer, starting from a default
|
||||
func (app *BaseApp) RegisterCodespace(codespace sdk.CodespaceType) sdk.CodespaceType {
|
||||
return app.codespacer.RegisterNext(codespace)
|
||||
}
|
||||
|
||||
// Mount a store to the provided key in the BaseApp multistore
|
||||
func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) {
|
||||
for _, key := range keys {
|
||||
|
@ -103,7 +114,6 @@ func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) {
|
|||
func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) {
|
||||
app.anteHandler = ah
|
||||
}
|
||||
|
||||
func (app *BaseApp) Router() Router { return app.router }
|
||||
|
||||
// load latest application version
|
||||
|
@ -311,9 +321,8 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
|
|||
if result.IsOK() {
|
||||
app.valUpdates = append(app.valUpdates, result.ValidatorUpdates...)
|
||||
} else {
|
||||
// Even though the Code is not OK, there will be some side
|
||||
// effects, like those caused by fee deductions or sequence
|
||||
// incrementations.
|
||||
// Even though the Result.Code is not OK, there are still effects,
|
||||
// namely fee deductions and sequence incrementing.
|
||||
}
|
||||
|
||||
// Tell the blockchain engine (i.e. Tendermint).
|
||||
|
@ -327,7 +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) {
|
||||
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.
|
||||
err := msg.ValidateBasic()
|
||||
if err != nil {
|
||||
err = err.WithDefaultCodespace(sdk.CodespaceRoot)
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
|
@ -441,7 +451,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
|
|||
// Use the header from this latest block.
|
||||
app.setCheckState(header)
|
||||
|
||||
// Emtpy the Deliver state
|
||||
// Empty the Deliver state
|
||||
app.deliverState = nil
|
||||
|
||||
return abci.ResponseCommit{
|
||||
|
|
|
@ -240,7 +240,7 @@ func TestDeliverTx(t *testing.T) {
|
|||
height := int64((counter / txPerHeight) + 1)
|
||||
assert.Equal(t, height, thisHeader.Height)
|
||||
|
||||
counter += 1
|
||||
counter++
|
||||
return sdk.Result{}
|
||||
})
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package core
|
||||
package context
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -126,7 +126,14 @@ func (ctx CoreContext) SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *w
|
|||
}
|
||||
|
||||
// sign and build the transaction from the msg
|
||||
func (ctx CoreContext) SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
func (ctx CoreContext) EnsureSignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (res *ctypes.ResultBroadcastTxCommit, err error) {
|
||||
|
||||
// default to next sequence number if none provided
|
||||
ctx, err = EnsureSequence(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
passphrase, err := ctx.GetPassphraseFromStdin(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -141,17 +148,17 @@ func (ctx CoreContext) SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Co
|
|||
}
|
||||
|
||||
// get the next sequence for the account address
|
||||
func (c CoreContext) NextSequence(address []byte) (int64, error) {
|
||||
if c.Decoder == nil {
|
||||
func (ctx CoreContext) NextSequence(address []byte) (int64, error) {
|
||||
if ctx.Decoder == nil {
|
||||
return 0, errors.New("AccountDecoder required but not provided")
|
||||
}
|
||||
|
||||
res, err := c.Query(address, c.AccountStore)
|
||||
res, err := ctx.Query(address, ctx.AccountStore)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
account, err := c.Decoder(res)
|
||||
account, err := ctx.Decoder(res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package core
|
||||
package context
|
||||
|
||||
import (
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
|
@ -6,6 +6,7 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// typical context created in sdk modules for transactions/queries
|
||||
type CoreContext struct {
|
||||
ChainID string
|
||||
Height int64
|
|
@ -2,6 +2,7 @@ package context
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
|
@ -9,11 +10,10 @@ import (
|
|||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/core"
|
||||
)
|
||||
|
||||
// NewCoreContextFromViper - return a new context with parameters from the command line
|
||||
func NewCoreContextFromViper() core.CoreContext {
|
||||
func NewCoreContextFromViper() CoreContext {
|
||||
nodeURI := viper.GetString(client.FlagNode)
|
||||
var rpc rpcclient.Client
|
||||
if nodeURI != "" {
|
||||
|
@ -27,7 +27,7 @@ func NewCoreContextFromViper() core.CoreContext {
|
|||
chainID = def
|
||||
}
|
||||
}
|
||||
return core.CoreContext{
|
||||
return CoreContext{
|
||||
ChainID: chainID,
|
||||
Height: viper.GetInt64(client.FlagHeight),
|
||||
TrustNode: viper.GetBool(client.FlagTrustNode),
|
||||
|
@ -54,7 +54,7 @@ func defaultChainID() (string, error) {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
return ctx, nil
|
||||
}
|
||||
|
|
|
@ -135,14 +135,17 @@ func printCreate(info keys.Info, seed string) {
|
|||
}
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// REST
|
||||
|
||||
// new key request REST body
|
||||
type NewKeyBody struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
Seed string `json:"seed"`
|
||||
}
|
||||
|
||||
// add new key REST handler
|
||||
func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var kb keys.Keybase
|
||||
var m NewKeyBody
|
||||
|
@ -208,6 +211,7 @@ func getSeed(algo keys.CryptoAlgo) string {
|
|||
return seed
|
||||
}
|
||||
|
||||
// Seed REST request handler
|
||||
func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
algoType := vars["type"]
|
||||
|
|
|
@ -48,12 +48,15 @@ func runDeleteCmd(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
// REST
|
||||
|
||||
// delete key request REST body
|
||||
type DeleteKeyBody struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// delete key REST handler
|
||||
func DeleteKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
|
|
|
@ -31,8 +31,10 @@ func runListCmd(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
//REST
|
||||
/////////////////////////
|
||||
// REST
|
||||
|
||||
// query key list REST handler
|
||||
func QueryKeysRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
kb, err := GetKeyBase()
|
||||
if err != nil {
|
||||
|
|
|
@ -29,6 +29,7 @@ func Commands() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// resgister REST routes
|
||||
func RegisterRoutes(r *mux.Router) {
|
||||
r.HandleFunc("/keys", QueryKeysRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/keys", AddNewKeyRequestHandler).Methods("POST")
|
||||
|
|
|
@ -42,8 +42,10 @@ func runShowCmd(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
// REST
|
||||
|
||||
// get key REST handler
|
||||
func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
|
|
|
@ -53,13 +53,16 @@ func runUpdateCmd(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// REST
|
||||
|
||||
// update key request REST body
|
||||
type UpdateKeyBody struct {
|
||||
NewPassword string `json:"new_password"`
|
||||
OldPassword string `json:"old_password"`
|
||||
}
|
||||
|
||||
// update key REST handler
|
||||
func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
name := vars["name"]
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package keys
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
|
@ -16,20 +19,10 @@ import (
|
|||
// KeyDBName is the directory under root where we store the keys
|
||||
const KeyDBName = "keys"
|
||||
|
||||
var (
|
||||
// keybase is used to make GetKeyBase a singleton
|
||||
keybase keys.Keybase
|
||||
)
|
||||
// keybase is used to make GetKeyBase a singleton
|
||||
var keybase keys.Keybase
|
||||
|
||||
// used for outputting keys.Info over REST
|
||||
type KeyOutput struct {
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
// TODO add pubkey?
|
||||
// Pubkey string `json:"pubkey"`
|
||||
}
|
||||
|
||||
// GetKeyBase initializes a keybase based on the configuration
|
||||
// initialize a keybase based on the configuration
|
||||
func GetKeyBase() (keys.Keybase, error) {
|
||||
if keybase == nil {
|
||||
rootDir := viper.GetString(cli.HomeFlag)
|
||||
|
@ -47,36 +40,57 @@ func SetKeyBase(kb keys.Keybase) {
|
|||
keybase = kb
|
||||
}
|
||||
|
||||
// used for outputting keys.Info over REST
|
||||
type KeyOutput struct {
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
PubKey string `json:"pub_key"`
|
||||
}
|
||||
|
||||
func NewKeyOutput(info keys.Info) KeyOutput {
|
||||
return KeyOutput{
|
||||
Name: info.Name,
|
||||
Address: info.PubKey.Address().String(),
|
||||
PubKey: strings.ToUpper(hex.EncodeToString(info.PubKey.Bytes())),
|
||||
}
|
||||
}
|
||||
|
||||
func NewKeyOutputs(infos []keys.Info) []KeyOutput {
|
||||
kos := make([]KeyOutput, len(infos))
|
||||
for i, info := range infos {
|
||||
kos[i] = NewKeyOutput(info)
|
||||
}
|
||||
return kos
|
||||
}
|
||||
|
||||
func printInfo(info keys.Info) {
|
||||
ko := NewKeyOutput(info)
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
addr := info.PubKey.Address().String()
|
||||
sep := "\t\t"
|
||||
if len(info.Name) > 7 {
|
||||
sep = "\t"
|
||||
}
|
||||
fmt.Printf("%s%s%s\n", info.Name, sep, addr)
|
||||
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\tPUBKEY:\n")
|
||||
fmt.Printf("%s\t%s\t%s\n", ko.Name, ko.Address, ko.PubKey)
|
||||
case "json":
|
||||
json, err := MarshalJSON(info)
|
||||
out, err := json.MarshalIndent(ko, "", "\t")
|
||||
if err != nil {
|
||||
panic(err) // really shouldn't happen...
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(json))
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func printInfos(infos []keys.Info) {
|
||||
kos := NewKeyOutputs(infos)
|
||||
switch viper.Get(cli.OutputFlag) {
|
||||
case "text":
|
||||
fmt.Println("All keys:")
|
||||
for _, i := range infos {
|
||||
printInfo(i)
|
||||
fmt.Printf("NAME:\tADDRESS:\t\t\t\t\tPUBKEY:\n")
|
||||
for _, ko := range kos {
|
||||
fmt.Printf("%s\t%s\t%s\n", ko.Name, ko.Address, ko.PubKey)
|
||||
}
|
||||
case "json":
|
||||
json, err := MarshalJSON(infos)
|
||||
out, err := json.MarshalIndent(kos, "", "\t")
|
||||
if err != nil {
|
||||
panic(err) // really shouldn't happen...
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(json))
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,12 @@ func init() {
|
|||
wire.RegisterCrypto(cdc)
|
||||
}
|
||||
|
||||
// marshal keys
|
||||
func MarshalJSON(o interface{}) ([]byte, error) {
|
||||
return cdc.MarshalJSON(o)
|
||||
}
|
||||
|
||||
// unmarshal json
|
||||
func UnmarshalJSON(bz []byte, ptr interface{}) error {
|
||||
return cdc.UnmarshalJSON(bz, ptr)
|
||||
}
|
||||
|
|
|
@ -28,10 +28,9 @@ func waitForRPC() {
|
|||
_, err := client.Call("status", map[string]interface{}{}, result)
|
||||
if err == nil {
|
||||
return
|
||||
} else {
|
||||
fmt.Println("error", err)
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
fmt.Println("error", err)
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,9 +42,9 @@ var (
|
|||
coinAmount = int64(10000000)
|
||||
|
||||
// XXX bad globals
|
||||
name = "test"
|
||||
password = "0123456789"
|
||||
port string // XXX: but it's the int ...
|
||||
name string = "test"
|
||||
password string = "0123456789"
|
||||
seed string
|
||||
sendAddr string
|
||||
)
|
||||
|
@ -80,7 +80,7 @@ func TestKeys(t *testing.T) {
|
|||
jsonStr = []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed": "%s"}`, newName, newPassword, newSeed))
|
||||
res, body = request(t, port, "POST", "/keys", jsonStr)
|
||||
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
addr := body
|
||||
assert.Len(t, addr, 40, "Returned address has wrong format", addr)
|
||||
|
||||
|
@ -311,7 +311,11 @@ func TestTxs(t *testing.T) {
|
|||
// strt TM and the LCD in process, listening on their respective sockets
|
||||
func startTMAndLCD() (*nm.Node, net.Listener, error) {
|
||||
|
||||
viper.Set(cli.HomeFlag, os.TempDir())
|
||||
dir, err := ioutil.TempDir("", "lcd_test")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
viper.Set(cli.HomeFlag, dir)
|
||||
kb, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
|
|
@ -66,7 +66,7 @@ func startRESTServerFn(cdc *wire.Codec) func(cmd *cobra.Command, args []string)
|
|||
|
||||
func createHandler(cdc *wire.Codec) http.Handler {
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/version", version.VersionRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/version", version.RequestHandler).Methods("GET")
|
||||
|
||||
kb, err := keys.GetKeyBase() //XXX
|
||||
if err != nil {
|
||||
|
|
|
@ -55,6 +55,7 @@ func getBlock(height *int64) ([]byte, error) {
|
|||
return output, nil
|
||||
}
|
||||
|
||||
// get the current blockchain height
|
||||
func GetChainHeight() (int64, error) {
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
if err != nil {
|
||||
|
@ -94,6 +95,7 @@ func printBlock(cmd *cobra.Command, args []string) error {
|
|||
|
||||
// REST
|
||||
|
||||
// REST handler to get a block
|
||||
func BlockRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
height, err := strconv.ParseInt(vars["height"], 10, 64)
|
||||
|
@ -117,6 +119,7 @@ func BlockRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write(output)
|
||||
}
|
||||
|
||||
// REST handler to get the latest block
|
||||
func LatestBlockRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
height, err := GetChainHeight()
|
||||
if err != nil {
|
||||
|
|
|
@ -44,11 +44,12 @@ func initClientCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// Register REST endpoints
|
||||
func RegisterRoutes(r *mux.Router) {
|
||||
r.HandleFunc("/node_info", NodeInfoRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/syncing", NodeSyncingRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/blocks/latest", LatestBlockRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/blocks/{height}", BlockRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/validatorsets/latest", LatestValidatorsetRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/validatorsets/{height}", ValidatorsetRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/validatorsets/latest", LatestValidatorSetRequestHandler).Methods("GET")
|
||||
r.HandleFunc("/validatorsets/{height}", ValidatorSetRequestHandler).Methods("GET")
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ func printNodeStatus(cmd *cobra.Command, args []string) error {
|
|||
|
||||
// REST
|
||||
|
||||
// REST handler for node info
|
||||
func NodeInfoRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
status, err := getNodeStatus()
|
||||
if err != nil {
|
||||
|
@ -69,6 +70,7 @@ func NodeInfoRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write(output)
|
||||
}
|
||||
|
||||
// REST handler for node syncing
|
||||
func NodeSyncingRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
status, err := getNodeStatus()
|
||||
if err != nil {
|
||||
|
|
|
@ -12,6 +12,8 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
)
|
||||
|
||||
// TODO these next two functions feel kinda hacky based on their placement
|
||||
|
||||
func validatorCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "validatorset <height>",
|
||||
|
@ -24,7 +26,7 @@ func validatorCommand() *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func GetValidators(height *int64) ([]byte, error) {
|
||||
func getValidators(height *int64) ([]byte, error) {
|
||||
// get the node
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -70,7 +72,8 @@ func printValidators(cmd *cobra.Command, args []string) error {
|
|||
|
||||
// REST
|
||||
|
||||
func ValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Validator Set at a height REST handler
|
||||
func ValidatorSetRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
height, err := strconv.ParseInt(vars["height"], 10, 64)
|
||||
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."))
|
||||
return
|
||||
}
|
||||
output, err := GetValidators(&height)
|
||||
output, err := getValidators(&height)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -93,14 +96,15 @@ func ValidatorsetRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
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()
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
output, err := GetValidators(&height)
|
||||
output, err := getValidators(&height)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -7,10 +7,12 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
)
|
||||
|
||||
// Tx Broadcast Body
|
||||
type BroadcastTxBody struct {
|
||||
TxBytes string `json="tx"`
|
||||
}
|
||||
|
||||
// BroadcastTx REST Handler
|
||||
func BroadcastTxRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var m BroadcastTxBody
|
||||
|
||||
|
|
|
@ -21,19 +21,37 @@ import (
|
|||
)
|
||||
|
||||
// Get the default command for a tx query
|
||||
func QueryTxCmd(cmdr commander) *cobra.Command {
|
||||
func QueryTxCmd(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "tx [hash]",
|
||||
Short: "Matches this txhash over all committed blocks",
|
||||
RunE: cmdr.queryAndPrintTx,
|
||||
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")
|
||||
|
||||
// TODO: change this to false when we can
|
||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c commander) queryTx(hashHexStr string, trustNode bool) ([]byte, error) {
|
||||
func queryTx(cdc *wire.Codec, hashHexStr string, trustNode bool) ([]byte, error) {
|
||||
hash, err := hex.DecodeString(hashHexStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -49,7 +67,7 @@ func (c commander) queryTx(hashHexStr string, trustNode bool) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info, err := formatTxResult(c.cdc, res)
|
||||
info, err := formatTxResult(cdc, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -88,31 +106,10 @@ func parseTx(cdc *wire.Codec, txBytes []byte) (sdk.Tx, error) {
|
|||
return tx, nil
|
||||
}
|
||||
|
||||
// CMD
|
||||
|
||||
// command to query for a transaction
|
||||
func (c commander) queryAndPrintTx(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 || len(args[0]) == 0 {
|
||||
return errors.New("You must provide a tx hash")
|
||||
}
|
||||
|
||||
// find the key to look up the account
|
||||
hashHexStr := args[0]
|
||||
trustNode := viper.GetBool(client.FlagTrustNode)
|
||||
|
||||
output, err := c.queryTx(hashHexStr, trustNode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// REST
|
||||
|
||||
// transaction query REST handler
|
||||
func QueryTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Request) {
|
||||
c := commander{cdc}
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
hashHexStr := vars["hash"]
|
||||
|
@ -122,7 +119,7 @@ func QueryTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Requ
|
|||
trustNode = true
|
||||
}
|
||||
|
||||
output, err := c.queryTx(hashHexStr, trustNode)
|
||||
output, err := queryTx(cdc, hashHexStr, trustNode)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -7,23 +7,18 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
// type used to pass around the provided cdc
|
||||
type commander struct {
|
||||
cdc *wire.Codec
|
||||
}
|
||||
|
||||
// AddCommands adds a number of tx-query related subcommands
|
||||
func AddCommands(cmd *cobra.Command, cdc *wire.Codec) {
|
||||
cmdr := commander{cdc}
|
||||
cmd.AddCommand(
|
||||
SearchTxCmd(cmdr),
|
||||
QueryTxCmd(cmdr),
|
||||
SearchTxCmd(cdc),
|
||||
QueryTxCmd(cdc),
|
||||
)
|
||||
}
|
||||
|
||||
// register REST routes
|
||||
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", SearchTxRequestHandler(cdc)).Methods("GET")
|
||||
// r.HandleFunc("/txs/sign", SignTxRequstHandler).Methods("POST")
|
||||
// r.HandleFunc("/txs/broadcast", BroadcastTxRequestHandler).Methods("POST")
|
||||
}
|
||||
|
|
|
@ -22,13 +22,24 @@ const (
|
|||
)
|
||||
|
||||
// default client command to search through tagged transactions
|
||||
func SearchTxCmd(cmdr commander) *cobra.Command {
|
||||
func SearchTxCmd(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "txs",
|
||||
Short: "Search for all transactions that match the given tags",
|
||||
RunE: cmdr.searchAndPrintTx,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
tags := viper.GetStringSlice(flagTags)
|
||||
|
||||
output, err := searchTx(cdc, tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
|
||||
|
||||
// TODO: change this to false once proofs built in
|
||||
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
|
||||
cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)")
|
||||
|
@ -36,7 +47,7 @@ func SearchTxCmd(cmdr commander) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
func (c commander) searchTx(tags []string) ([]byte, error) {
|
||||
func searchTx(cdc *wire.Codec, tags []string) ([]byte, error) {
|
||||
if len(tags) == 0 {
|
||||
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
|
||||
}
|
||||
|
||||
info, err := formatTxResults(c.cdc, res)
|
||||
info, err := formatTxResults(cdc, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output, err := c.cdc.MarshalJSON(info)
|
||||
output, err := cdc.MarshalJSON(info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -79,24 +90,11 @@ func formatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]txInfo, error)
|
|||
return out, nil
|
||||
}
|
||||
|
||||
// CMD
|
||||
|
||||
func (c commander) searchAndPrintTx(cmd *cobra.Command, args []string) error {
|
||||
tags := viper.GetStringSlice(flagTags)
|
||||
|
||||
output, err := c.searchTx(tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
// REST
|
||||
|
||||
// Search Tx REST Handler
|
||||
func SearchTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Request) {
|
||||
c := commander{cdc}
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
tag := r.FormValue("tag")
|
||||
if tag == "" {
|
||||
|
@ -106,7 +104,7 @@ func SearchTxRequestHandler(cdc *wire.Codec) func(http.ResponseWriter, *http.Req
|
|||
}
|
||||
|
||||
tags := []string{tag}
|
||||
output, err := c.searchTx(tags)
|
||||
output, err := searchTx(cdc, tags)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -8,12 +8,15 @@ import (
|
|||
keys "github.com/tendermint/go-crypto/keys"
|
||||
)
|
||||
|
||||
// REST request body
|
||||
// TODO does this need to be exposed?
|
||||
type SignTxBody struct {
|
||||
Name string `json="name"`
|
||||
Password string `json="password"`
|
||||
TxBytes string `json="tx"`
|
||||
}
|
||||
|
||||
// sign transaction REST Handler
|
||||
func SignTxRequstHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var kb keys.Keybase
|
||||
var m SignTxBody
|
||||
|
|
|
@ -0,0 +1,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/rpc"
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
|
||||
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/commands"
|
||||
simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands"
|
||||
stakecmd "github.com/cosmos/cosmos-sdk/x/stake/commands"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
)
|
||||
|
||||
// TODO: distinguish from basecli
|
||||
|
||||
// rootCmd is the entry point for this binary
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
|
@ -34,10 +30,7 @@ var (
|
|||
)
|
||||
|
||||
func main() {
|
||||
// disable sorting
|
||||
cobra.EnableCommandSorting = false
|
||||
|
||||
// get the codec
|
||||
cdc := app.MakeCodec()
|
||||
|
||||
// TODO: setup keybase, viper object, etc. to be passed into
|
||||
|
@ -53,24 +46,21 @@ func main() {
|
|||
// add query/post commands (custom to binary)
|
||||
rootCmd.AddCommand(
|
||||
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(
|
||||
client.PostCommands(
|
||||
bankcmd.SendTxCmd(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCTransferCmd(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCRelayCmd(cdc),
|
||||
simplestakingcmd.BondTxCmd(cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
simplestakingcmd.UnbondTxCmd(cdc),
|
||||
stakecmd.GetCmdDeclareCandidacy(cdc),
|
||||
stakecmd.GetCmdEditCandidacy(cdc),
|
||||
stakecmd.GetCmdDelegate(cdc),
|
||||
stakecmd.GetCmdUnbond(cdc),
|
||||
)...)
|
||||
|
||||
// add proxy, version and key info
|
||||
|
@ -83,6 +73,6 @@ func main() {
|
|||
)
|
||||
|
||||
// prepare and add flags
|
||||
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.gaiacli"))
|
||||
executor := cli.PrepareMainCmd(rootCmd, "GA", os.ExpandEnv("$HOME/.gaiacli"))
|
||||
executor.Execute()
|
||||
}
|
|
@ -11,7 +11,7 @@ import (
|
|||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -25,21 +25,21 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// TODO: distinguish from basecoin
|
||||
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||
dataDir := filepath.Join(rootDir, "data")
|
||||
db, err := dbm.NewGoLevelDB("gaia", dataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bapp := app.NewBasecoinApp(logger, db)
|
||||
bapp := app.NewGaiaApp(logger, db)
|
||||
return bapp, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context)
|
||||
server.AddCommands(rootCmd, app.DefaultGenAppState, generateApp, context)
|
||||
|
||||
// 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()
|
||||
}
|
|
@ -47,7 +47,7 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'Cosmos-SDK'
|
||||
copyright = u'2017, The Authors'
|
||||
copyright = u'2018, The Authors'
|
||||
author = u'The Authors'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
@ -69,7 +69,7 @@ language = None
|
|||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'old']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
|
|
@ -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`:
|
||||
|
||||
```go
|
||||
type SendMsg struct {
|
||||
type MsgSend struct {
|
||||
Inputs []Input `json:"inputs"`
|
||||
Outputs []Output `json:"outputs"`
|
||||
}
|
||||
|
||||
type IssueMsg struct {
|
||||
type MsgIssue struct {
|
||||
Banker sdk.Address `json:"banker"`
|
||||
Outputs []Output `json:"outputs"`
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ type IssueMsg struct {
|
|||
Each specifies the addresses that must sign the message:
|
||||
|
||||
```go
|
||||
func (msg SendMsg) GetSigners() []sdk.Address {
|
||||
func (msg MsgSend) GetSigners() []sdk.Address {
|
||||
addrs := make([]sdk.Address, len(msg.Inputs))
|
||||
for i, in := range msg.Inputs {
|
||||
addrs[i] = in.Address
|
||||
|
@ -91,7 +91,7 @@ func (msg SendMsg) GetSigners() []sdk.Address {
|
|||
return addrs
|
||||
}
|
||||
|
||||
func (msg IssueMsg) GetSigners() []sdk.Address {
|
||||
func (msg MsgIssue) GetSigners() []sdk.Address {
|
||||
return []sdk.Address{msg.Banker}
|
||||
}
|
||||
```
|
||||
|
@ -174,13 +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
|
||||
`Codec` object, so as not to taint the global name space.
|
||||
|
||||
For instance, in `Basecoin`, we wish to register the `SendMsg` and `IssueMsg`
|
||||
For instance, in `Basecoin`, we wish to register the `MsgSend` and `MsgIssue`
|
||||
types:
|
||||
|
||||
```go
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(bank.SendMsg{}, "cosmos-sdk/SendMsg", nil)
|
||||
cdc.RegisterConcrete(bank.IssueMsg{}, "cosmos-sdk/IssueMsg", nil)
|
||||
cdc.RegisterConcrete(bank.MsgSend{}, "cosmos-sdk/MsgSend", nil)
|
||||
cdc.RegisterConcrete(bank.MsgIssue{}, "cosmos-sdk/MsgIssue", nil)
|
||||
```
|
||||
|
||||
Note how each concrete type is given a name - these name determine the type's
|
||||
|
@ -319,8 +319,8 @@ func NewHandler(am sdk.AccountMapper) sdk.Handler {
|
|||
The quintessential SDK application is Basecoin - a simple
|
||||
multi-asset cryptocurrency. Basecoin consists of a set of
|
||||
accounts stored in a Merkle tree, where each account may have
|
||||
many coins. There are two message types: SendMsg and IssueMsg.
|
||||
SendMsg allows coins to be sent around, while IssueMsg allows a
|
||||
many coins. There are two message types: MsgSend and MsgIssue.
|
||||
MsgSend allows coins to be sent around, while MsgIssue allows a
|
||||
set of predefined users to issue new coins.
|
||||
|
||||
## Conclusion
|
||||
|
|
|
@ -14,14 +14,13 @@ Welcome to the Cosmos SDK!
|
|||
SDK
|
||||
---
|
||||
|
||||
.. One maxdepth for now
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
sdk/overview.rst
|
||||
sdk/install.rst
|
||||
sdk/glossary.rst
|
||||
sdk/key-management.rst
|
||||
.. sdk/overview.rst # needs to be updated
|
||||
.. old/glossary.rst # not completely up to date but has good content
|
||||
|
||||
.. Basecoin
|
||||
.. --------
|
||||
|
@ -29,19 +28,17 @@ SDK
|
|||
.. .. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
.. basecoin/basics.rst
|
||||
.. basecoin/extensions.rst
|
||||
.. old/basecoin/basics.rst # has a decent getting-start tutorial that's relatively up to date, should be consolidated with the other getting started doc
|
||||
|
||||
Extensions
|
||||
----------
|
||||
.. Extensions
|
||||
.. ----------
|
||||
|
||||
Replay Protection
|
||||
~~~~~~~~~~~~~~~~~
|
||||
.. old/basecoin/extensions.rst # probably not worth salvaging
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
.. Replay Protection
|
||||
.. ~~~~~~~~~~~~~~~~~
|
||||
|
||||
x/replay-protection.rst
|
||||
.. old/replay-protection.rst # not sure if worth salvaging
|
||||
|
||||
|
||||
Staking
|
||||
|
@ -50,17 +47,13 @@ Staking
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
staking/intro.rst
|
||||
staking/key-management.rst
|
||||
staking/local-testnet.rst
|
||||
staking/public-testnet.rst
|
||||
staking/testnet.rst
|
||||
.. staking/intro.rst
|
||||
.. staking/key-management.rst
|
||||
.. staking/local-testnet.rst
|
||||
.. staking/public-testnet.rst
|
||||
|
||||
Extras
|
||||
------
|
||||
.. IBC
|
||||
.. ---
|
||||
|
||||
.. One maxdepth for now
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ibc.rst
|
||||
.. old/ibc.rst # needs to be updated
|
||||
|
|
|
@ -15,7 +15,7 @@ while defining as little about that state machine as possible (staying true to t
|
|||
BaseApp requires stores to be mounted via capabilities keys - handlers can only access
|
||||
stores they're given the key for. The BaseApp ensures all stores are properly loaded, cached, and committed.
|
||||
One mounted store is considered the "main" - it holds the latest block header, from which we can find and load the
|
||||
most recent state.
|
||||
most recent state ([TODO](https://github.com/cosmos/cosmos-sdk/issues/522)).
|
||||
|
||||
BaseApp distinguishes between two handler types - the `AnteHandler` and the `MsgHandler`.
|
||||
The former is a global validity check (checking nonces, sigs and sufficient balances to pay fees,
|
|
@ -1,16 +1,12 @@
|
|||
Install
|
||||
=======
|
||||
|
||||
If you aren't used to compile go programs and just want the released
|
||||
version of the code, please head to our
|
||||
`downloads <https://tendermint.com/download>`__ page to get a
|
||||
pre-compiled binary for your platform.
|
||||
|
||||
Usually, Cosmos SDK can be installed like a normal Go program:
|
||||
Cosmos SDK can be installed to
|
||||
``$GOPATH/src/github.com/cosmos/cosmos-sdk`` like a normal Go program:
|
||||
|
||||
::
|
||||
|
||||
go get -u github.com/cosmos/cosmos-sdk
|
||||
go get github.com/cosmos/cosmos-sdk
|
||||
|
||||
If the dependencies have been updated with breaking changes, or if
|
||||
another branch is required, ``dep`` is used for dependency management.
|
||||
|
@ -20,16 +16,33 @@ repo, the correct way to install is:
|
|||
::
|
||||
|
||||
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
|
||||
git pull origin master
|
||||
make all
|
||||
make get_vendor_deps
|
||||
make install
|
||||
make install_examples
|
||||
|
||||
This will create the ``basecoin`` binary in ``$GOPATH/bin``.
|
||||
``make all`` implies ``make get_vendor_deps`` and uses ``dep`` to
|
||||
install the correct version of all dependencies. It also tests the code,
|
||||
including some cli tests to make sure your binary behaves properly.
|
||||
This will install ``gaiad`` and ``gaiacli`` and four example binaries:
|
||||
``basecoind``, ``basecli``, ``democoind``, and ``democli``.
|
||||
|
||||
If you need another branch, make sure to run ``git checkout <branch>``
|
||||
before ``make all``. And if you switch branches a lot, especially
|
||||
touching other tendermint repos, you may need to ``make fresh``
|
||||
sometimes so dep doesn't get confused with all the branches and
|
||||
versions lying around.
|
||||
Verify that everything is OK by running:
|
||||
|
||||
::
|
||||
|
||||
gaiad version
|
||||
|
||||
you should see:
|
||||
|
||||
::
|
||||
|
||||
0.15.0-rc1-9d90c6b
|
||||
|
||||
then with:
|
||||
|
||||
::
|
||||
|
||||
gaiacli version
|
||||
|
||||
you should see:
|
||||
|
||||
::
|
||||
|
||||
0.15.0-rc1-9d90c6b
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
Key Management
|
||||
==============
|
||||
|
||||
Here we cover many aspects of handling keys within the Cosmos SDK framework.
|
||||
|
||||
Pseudo Code
|
||||
-----------
|
||||
|
||||
Generating an address for an ed25519 public key (in pseudo code):
|
||||
|
||||
::
|
||||
|
||||
const TypeDistinguisher = HexToBytes("1624de6220")
|
||||
|
||||
// prepend the TypeDistinguisher as Bytes
|
||||
SerializedBytes = TypeDistinguisher ++ PubKey.asBytes()
|
||||
|
||||
Address = ripemd160(SerializedBytes)
|
|
@ -1,10 +1,17 @@
|
|||
# IBC Specification
|
||||
|
||||
IBC(Inter-Blockchain Communication) protocol is used by multiple zones on Cosmos. Using IBC, the zones can send coins or arbitrary data to other zones.
|
||||
The IBC (Inter Blockchain Communication) protocol specifies how tokens,
|
||||
non-fungible assets and complex objects can be moved securely between different
|
||||
zones (independent blockchains). IBC is conceptually similar to TCP/IP in the
|
||||
sense that anyone can implement it in order to be able to establish IBC
|
||||
connections with willing clients.
|
||||
|
||||
|
||||
## Terms
|
||||
|
||||
How IBC module treats incoming IBC packets is simillar with how BaseApp treats incoming transactions. Therefore, the components of IBC module have their corresponding pair in BaseApp.
|
||||
How IBC module treats incoming IBC packets is similar to how BaseApp treats
|
||||
incoming transactions. Therefore, the components of IBC module have their
|
||||
corresponding pair in BaseApp.
|
||||
|
||||
| BaseApp Terms | IBC Terms |
|
||||
| ------------- | ---------- |
|
||||
|
@ -12,20 +19,27 @@ How IBC module treats incoming IBC packets is simillar with how BaseApp treats i
|
|||
| Tx | Packet |
|
||||
| Msg | Payload |
|
||||
|
||||
|
||||
## MVP Specifications
|
||||
|
||||
### [MVP1](./mvp1.md)
|
||||
|
||||
MVP1 will contain the basic functionalities, including packet generation and packet receivement. There will be no security check for incoming packets.
|
||||
MVP1 will contain the basic functionalities, including packet generation and
|
||||
incoming packet processing. There will be no security check for incoming
|
||||
packets.
|
||||
|
||||
### [MVP2](./mvp2.md)
|
||||
|
||||
IBC module will be more modular in MVP2. Indivisual modules can register custom handlers to IBC module.
|
||||
The IBC module will be more modular in MVP2. Individual modules can register
|
||||
custom handlers on the IBC module.
|
||||
|
||||
### [MVP3](./mvp3.md)
|
||||
|
||||
Light client verification is added to verify the message from the other chain. Registering chains with their ROT(Root Of Trust) is needed.
|
||||
Light client verification is added to verify an IBC packet from another chain.
|
||||
Registering chains with their RoT(Root of Trust) is added as well.
|
||||
|
||||
### [MVP4](./mvp4.md)
|
||||
|
||||
ACK verification / timeout handler helper functions and messaging queue are implemented to make it failsafe. Callbacks will be registered to the dispatcher to handle failure when they register handlers.
|
||||
ACK verification / timeout handler helper functions and messaging queues are
|
||||
implemented to make it safe. Callbacks will be registered to the dispatcher to
|
||||
handle failure when they register handlers.
|
||||
|
|
|
@ -1,77 +1,205 @@
|
|||
Using Gaia
|
||||
==========
|
||||
Using The Staking Module
|
||||
========================
|
||||
|
||||
This project is a demonstration of the Cosmos Hub with staking functionality; it is
|
||||
designed to get validator acquianted with staking concepts and procedure.
|
||||
This project is a demonstration of the Cosmos Hub staking functionality; it is
|
||||
designed to get validator acquianted with staking concepts and procedures.
|
||||
|
||||
Potential validators will be declaring their candidacy, after which users can
|
||||
delegate and, if they so wish, unbond. This can be practiced using a local or
|
||||
public testnet.
|
||||
|
||||
This example covers initial setup of a two-node testnet between a server in the cloud and a local machine. Begin this tutorial from a cloud machine that you've ``ssh``'d into.
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
The ``gaia`` tooling is an extension of the Cosmos-SDK; to install:
|
||||
The ``gaiad`` and ``gaiacli`` binaries:
|
||||
|
||||
::
|
||||
|
||||
go get github.com/cosmos/gaia
|
||||
cd $GOPATH/src/github.com/cosmos/gaia
|
||||
go get github.com/cosmos/cosmos-sdk
|
||||
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
|
||||
make get_vendor_deps
|
||||
make install
|
||||
|
||||
It has three primary commands:
|
||||
Let's jump right into it. First, we initialize some default files:
|
||||
|
||||
::
|
||||
|
||||
Available Commands:
|
||||
node The Cosmos Network delegation-game blockchain test
|
||||
rest-server REST client for gaia commands
|
||||
client Gaia light client
|
||||
|
||||
version Show version info
|
||||
help Help about any command
|
||||
gaiad init
|
||||
|
||||
and a handful of flags that are highlighted only as necessary.
|
||||
which will output:
|
||||
|
||||
The ``gaia node`` command is a proxt for running a tendermint node. You'll be using
|
||||
this command to either initialize a new node, or - using existing files - joining
|
||||
the testnet.
|
||||
::
|
||||
|
||||
The ``gaia rest-server`` command is used by the `cosmos UI <https://github.com/cosmos/cosmos-ui>`__.
|
||||
I[03-30|11:20:13.365] Found private validator module=main path=/root/.gaiad/config/priv_validator.json
|
||||
I[03-30|11:20:13.365] Found genesis file module=main path=/root/.gaiad/config/genesis.json
|
||||
Secret phrase to access coins:
|
||||
citizen hungry tennis noise park hire glory exercise link glow dolphin labor design grit apple abandon
|
||||
|
||||
Lastly, the ``gaia client`` command is the workhorse of the staking module. It allows
|
||||
for sending various transactions and other types of interaction with a running chain.
|
||||
that you've setup or joined a testnet.
|
||||
This tell us we have a ``priv_validator.json`` and ``genesis.json`` in the ``~/.gaiad/config`` directory. A ``config.toml`` was also created in the same directory. It is a good idea to get familiar with those files. Write down the seed.
|
||||
|
||||
Generating Keys
|
||||
---------------
|
||||
The next thing we'll need to is add the key from ``priv_validator.json`` to the ``gaiacli`` key manager. For this we need a seed and a password:
|
||||
|
||||
Review the `key management tutorial <../key-management.html>`__ and create one key
|
||||
if you'll be joining the public testnet, and three keys if you'll be trying out a local
|
||||
testnet.
|
||||
::
|
||||
|
||||
gaiacli keys add alice --recover
|
||||
|
||||
which will give you three prompts:
|
||||
|
||||
::
|
||||
|
||||
Enter a passphrase for your key:
|
||||
Repeat the passphrase:
|
||||
Enter your recovery seed phrase:
|
||||
|
||||
create a password and copy in your seed phrase. The name and address of the key will be output:
|
||||
|
||||
::
|
||||
NAME: ADDRESS: PUBKEY:
|
||||
alice 67997DD03D527EB439B7193F2B813B05B219CC02 1624DE6220BB89786C1D597050438C728202436552C6226AB67453CDB2A4D2703402FB52B6
|
||||
|
||||
You can see all available keys with:
|
||||
|
||||
::
|
||||
|
||||
gaiacli keys list
|
||||
|
||||
Setup Testnet
|
||||
-------------
|
||||
|
||||
The first thing you'll want to do is either `create a local testnet <./local-testnet.html>`__ or
|
||||
join a `public testnet <./public-testnet.html>`__. Either step is required before proceeding.
|
||||
Next, we start the daemon (do this in another window):
|
||||
|
||||
The rest of this tutorial will assume a local testnet with three participants: ``alice`` will be
|
||||
the initial validator, ``bob`` will first receives tokens from ``alice`` then declare candidacy
|
||||
as a validator, and ``charlie`` will bond then unbond to ``bob``. If you're joining the public
|
||||
testnet, the token amounts will need to be adjusted.
|
||||
::
|
||||
|
||||
gaiad start
|
||||
|
||||
and you'll see blocks start streaming through.
|
||||
|
||||
For this example, we're doing the above on a cloud machine. The next steps should be done on your local machine or another server in the cloud, which will join the running testnet then bond/unbond.
|
||||
|
||||
Accounts
|
||||
--------
|
||||
|
||||
We have:
|
||||
|
||||
- ``alice`` the initial validator (in the cloud)
|
||||
- ``bob`` receives tokens from ``alice`` then declares candidacy (from local machine)
|
||||
- ``charlie`` will bond and unbond to ``bob`` (from local machine)
|
||||
|
||||
Remember that ``alice`` was already created. On your second machine, install the binaries and create two new keys:
|
||||
|
||||
::
|
||||
|
||||
gaiacli keys add bob
|
||||
gaiacli keys add charlie
|
||||
|
||||
both of which will prompt you for a password. Now we need to copy the ``genesis.json`` and ``config.toml`` from the first machine (with ``alice``) to the second machine. This is a good time to look at both these files.
|
||||
|
||||
The ``genesis.json`` should look something like:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"app_state": {
|
||||
"accounts": [
|
||||
{
|
||||
"address": "1D9B2356CAADF46D3EE3488E3CCE3028B4283DEE",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "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
|
||||
--------------
|
||||
|
||||
We'll have ``alice`` who is currently quite rich, send some ``fermions`` to ``bob``:
|
||||
We'll have ``alice`` send some ``mycoin`` to ``bob``, who has now joined the network:
|
||||
|
||||
::
|
||||
|
||||
gaia client tx send --amount=1000fermion --sequence=1 --name=alice --to=5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6
|
||||
gaiacli send --amount=1000mycoin --sequence=0 --name=alice --to=5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6 --chain-id=test-chain-Uv1EVU
|
||||
|
||||
where the ``--sequence`` flag is to be incremented for each transaction, the ``--name`` flag names the sender, and the ``--to`` flag takes ``bob``'s address. You'll see something like:
|
||||
where the ``--sequence`` flag is to be incremented for each transaction, the ``--name`` flag is the sender (alice), and the ``--to`` flag takes ``bob``'s address. You'll see something like:
|
||||
|
||||
::
|
||||
|
||||
|
@ -101,19 +229,25 @@ where the ``--sequence`` flag is to be incremented for each transaction, the ``-
|
|||
"height": 2963
|
||||
}
|
||||
|
||||
Check out ``bob``'s account, which should now have 992 fermions:
|
||||
TODO: check the above with current actual output.
|
||||
|
||||
Check out ``bob``'s account, which should now have 1000 mycoin:
|
||||
|
||||
::
|
||||
|
||||
gaia client query account 5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6
|
||||
gaiacli account 5A35E4CC7B7DC0A5CB49CEA91763213A9AE92AD6
|
||||
|
||||
Adding a Second Validator
|
||||
-------------------------
|
||||
|
||||
**This section is wrong/needs to be updated**
|
||||
|
||||
Next, let's add the second node as a validator.
|
||||
|
||||
First, we need the pub_key data:
|
||||
|
||||
** need to make bob a priv_Val above?
|
||||
|
||||
::
|
||||
|
||||
cat $HOME/.gaia2/priv_validator.json
|
||||
|
@ -130,7 +264,7 @@ Now ``bob`` can declare candidacy to that pubkey:
|
|||
|
||||
::
|
||||
|
||||
gaia client tx declare-candidacy --amount=10fermion --name=bob --pubkey=<pub_key data> --moniker=bobby
|
||||
gaiacli declare-candidacy --amount=10mycoin --name=bob --pubkey=<pub_key data> --moniker=bobby
|
||||
|
||||
with an output like:
|
||||
|
||||
|
@ -147,11 +281,11 @@ with an output like:
|
|||
}
|
||||
|
||||
|
||||
We should see ``bob``'s account balance decrease by 10 fermions:
|
||||
We should see ``bob``'s account balance decrease by 10 mycoin:
|
||||
|
||||
::
|
||||
|
||||
gaia client query account 5D93A6059B6592833CBC8FA3DA90EE0382198985
|
||||
gaiacli account 5D93A6059B6592833CBC8FA3DA90EE0382198985
|
||||
|
||||
To confirm for certain the new validator is active, ask the tendermint node:
|
||||
|
||||
|
@ -163,7 +297,7 @@ If you now kill either node, blocks will stop streaming in, because
|
|||
there aren't enough validators online. Turn it back on and they will
|
||||
start streaming again.
|
||||
|
||||
Now that ``bob`` has declared candidacy, which essentially bonded 10 fermions and made him a validator, we're going to get ``charlie`` to delegate some coins to ``bob``.
|
||||
Now that ``bob`` has declared candidacy, which essentially bonded 10 mycoin and made him a validator, we're going to get ``charlie`` to delegate some coins to ``bob``.
|
||||
|
||||
Delegating
|
||||
----------
|
||||
|
@ -172,13 +306,13 @@ First let's have ``alice`` send some coins to ``charlie``:
|
|||
|
||||
::
|
||||
|
||||
gaia client tx send --amount=1000fermion --sequence=2 --name=alice --to=48F74F48281C89E5E4BE9092F735EA519768E8EF
|
||||
gaiacli tx --amount=1000mycoin --sequence=2 --name=alice --to=48F74F48281C89E5E4BE9092F735EA519768E8EF
|
||||
|
||||
Then ``charlie`` will delegate some fermions to ``bob``:
|
||||
Then ``charlie`` will delegate some mycoin to ``bob``:
|
||||
|
||||
::
|
||||
|
||||
gaia client tx delegate --amount=10fermion --name=charlie --pubkey=<pub_key data>
|
||||
gaiacli tx delegate --amount=10mycoin --name=charlie --pubkey=<pub_key data>
|
||||
|
||||
You'll see output like:
|
||||
|
||||
|
@ -194,13 +328,13 @@ You'll see output like:
|
|||
"height": 51585
|
||||
}
|
||||
|
||||
And that's it. You can query ``charlie``'s account to see the decrease in fermions.
|
||||
And that's it. You can query ``charlie``'s account to see the decrease in mycoin.
|
||||
|
||||
To get more information about the candidate, try:
|
||||
|
||||
::
|
||||
|
||||
gaia client query candidate --pubkey=<pub_key data>
|
||||
gaiacli query candidate --pubkey=<pub_key data>
|
||||
|
||||
and you'll see output similar to:
|
||||
|
||||
|
@ -233,7 +367,7 @@ It's also possible the query the delegator's bond like so:
|
|||
|
||||
::
|
||||
|
||||
gaia client query delegator-bond --delegator-address 48F74F48281C89E5E4BE9092F735EA519768E8EF --pubkey 52D6FCD8C92A97F7CCB01205ADF310A18411EA8FDCC10E65BF2FCDB05AD1689B
|
||||
gaiacli query delegator-bond --delegator-address 48F74F48281C89E5E4BE9092F735EA519768E8EF --pubkey 52D6FCD8C92A97F7CCB01205ADF310A18411EA8FDCC10E65BF2FCDB05AD1689B
|
||||
|
||||
with an output similar to:
|
||||
|
||||
|
@ -262,9 +396,7 @@ your VotingPower reduce and your account balance increase.
|
|||
|
||||
::
|
||||
|
||||
gaia client tx unbond --amount=5fermion --name=charlie --pubkey=<pub_key data>
|
||||
gaia client query account 48F74F48281C89E5E4BE9092F735EA519768E8EF
|
||||
gaiacli unbond --amount=5mycoin --name=charlie --pubkey=<pub_key data>
|
||||
gaiacli account 48F74F48281C89E5E4BE9092F735EA519768E8EF
|
||||
|
||||
See the bond decrease with ``gaia client query delegator-bond`` like above.
|
||||
|
||||
That concludes an overview of the ``gaia`` tooling for local testing.
|
||||
See the bond decrease with ``gaiacli query delegator-bond`` like above.
|
||||
|
|
|
@ -0,0 +1,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,
|
||||
app.capKeyMainStore, // target store
|
||||
&types.AppAccount{}, // prototype
|
||||
).Seal()
|
||||
)
|
||||
|
||||
// Add handlers.
|
||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
||||
coinKeeper := bank.NewKeeper(app.accountMapper)
|
||||
ibcMapper := ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace))
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
||||
|
@ -94,12 +94,12 @@ func MakeCodec() *wire.Codec {
|
|||
|
||||
// Register Msgs
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(bank.SendMsg{}, "basecoin/Send", nil)
|
||||
cdc.RegisterConcrete(bank.IssueMsg{}, "basecoin/Issue", nil)
|
||||
cdc.RegisterConcrete(bank.MsgSend{}, "basecoin/Send", nil)
|
||||
cdc.RegisterConcrete(bank.MsgIssue{}, "basecoin/Issue", nil)
|
||||
cdc.RegisterConcrete(ibc.IBCTransferMsg{}, "basecoin/IBCTransferMsg", nil)
|
||||
cdc.RegisterConcrete(ibc.IBCReceiveMsg{}, "basecoin/IBCReceiveMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.BondMsg{}, "basecoin/BondMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.UnbondMsg{}, "basecoin/UnbondMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.MsgBond{}, "basecoin/BondMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.MsgUnbond{}, "basecoin/UnbondMsg", nil)
|
||||
|
||||
// Register AppAccount
|
||||
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.
|
||||
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
|
||||
return nil, sdk.ErrTxDecode("").Trace(err.Error())
|
||||
}
|
||||
return tx, nil
|
||||
}
|
||||
|
|
|
@ -42,12 +42,12 @@ var (
|
|||
0,
|
||||
}
|
||||
|
||||
sendMsg1 = bank.SendMsg{
|
||||
sendMsg1 = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
|
||||
}
|
||||
|
||||
sendMsg2 = bank.SendMsg{
|
||||
sendMsg2 = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{
|
||||
bank.NewOutput(addr2, halfCoins),
|
||||
|
@ -55,7 +55,7 @@ var (
|
|||
},
|
||||
}
|
||||
|
||||
sendMsg3 = bank.SendMsg{
|
||||
sendMsg3 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr1, coins),
|
||||
bank.NewInput(addr4, coins),
|
||||
|
@ -66,7 +66,7 @@ var (
|
|||
},
|
||||
}
|
||||
|
||||
sendMsg4 = bank.SendMsg{
|
||||
sendMsg4 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr2, coins),
|
||||
},
|
||||
|
@ -75,7 +75,7 @@ var (
|
|||
},
|
||||
}
|
||||
|
||||
sendMsg5 = bank.SendMsg{
|
||||
sendMsg5 = bank.MsgSend{
|
||||
Inputs: []bank.Input{
|
||||
bank.NewInput(addr1, manyCoins),
|
||||
},
|
||||
|
@ -166,7 +166,7 @@ func TestSortGenesis(t *testing.T) {
|
|||
|
||||
// Unsorted coins means invalid
|
||||
err := sendMsg5.ValidateBasic()
|
||||
require.Equal(t, sdk.CodeInvalidCoins, err.ABCICode(), err.ABCILog())
|
||||
require.Equal(t, sdk.CodeInvalidCoins, err.Code(), err.ABCILog())
|
||||
|
||||
// Sort coins, should be valid
|
||||
sendMsg5.Inputs[0].Coins.Sort()
|
||||
|
@ -208,7 +208,7 @@ func TestGenesis(t *testing.T) {
|
|||
assert.Equal(t, acc, res1)
|
||||
}
|
||||
|
||||
func TestSendMsgWithAccounts(t *testing.T) {
|
||||
func TestMsgSendWithAccounts(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
// Construct some genesis bytes to reflect basecoin/types/AppAccount
|
||||
|
@ -243,13 +243,13 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
|||
tx.Signatures[0].Sequence = 1
|
||||
res := bapp.Deliver(tx)
|
||||
|
||||
assert.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
|
||||
|
||||
// resigning the tx with the bumped sequence should work
|
||||
SignCheckDeliver(t, bapp, sendMsg1, []int64{1}, true, priv1)
|
||||
}
|
||||
|
||||
func TestSendMsgMultipleOut(t *testing.T) {
|
||||
func TestMsgSendMultipleOut(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
genCoins, err := sdk.ParseCoins("42foocoin")
|
||||
|
@ -311,7 +311,7 @@ func TestSengMsgMultipleInOut(t *testing.T) {
|
|||
CheckBalance(t, bapp, addr3, "10foocoin")
|
||||
}
|
||||
|
||||
func TestSendMsgDependent(t *testing.T) {
|
||||
func TestMsgSendDependent(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
genCoins, err := sdk.ParseCoins("42foocoin")
|
||||
|
@ -339,7 +339,7 @@ func TestSendMsgDependent(t *testing.T) {
|
|||
CheckBalance(t, bapp, addr1, "42foocoin")
|
||||
}
|
||||
|
||||
func TestQuizMsg(t *testing.T) {
|
||||
func TestMsgQuiz(t *testing.T) {
|
||||
bapp := newBasecoinApp()
|
||||
|
||||
// Construct genesis state
|
||||
|
@ -437,18 +437,18 @@ func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq []int64,
|
|||
// Run a Check
|
||||
res := bapp.Check(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
// Simulate a Block
|
||||
bapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = bapp.Deliver(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
bapp.EndBlock(abci.RequestEndBlock{})
|
||||
//bapp.Commit()
|
||||
|
|
|
@ -66,14 +66,14 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
|
|||
cdc,
|
||||
app.capKeyMainStore, // target store
|
||||
&types.AppAccount{}, // prototype
|
||||
).Seal()
|
||||
)
|
||||
|
||||
// Add handlers.
|
||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
||||
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
|
||||
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper)
|
||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
||||
coinKeeper := bank.NewKeeper(app.accountMapper)
|
||||
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper, app.RegisterCodespace(cool.DefaultCodespace))
|
||||
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), coinKeeper, app.RegisterCodespace(pow.DefaultCodespace))
|
||||
ibcMapper := ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper, app.RegisterCodespace(simplestake.DefaultCodespace))
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||
AddRoute("cool", cool.NewHandler(coolKeeper)).
|
||||
|
@ -104,15 +104,15 @@ func MakeCodec() *wire.Codec {
|
|||
|
||||
// Register Msgs
|
||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||
cdc.RegisterConcrete(bank.SendMsg{}, "democoin/Send", nil)
|
||||
cdc.RegisterConcrete(bank.IssueMsg{}, "democoin/Issue", nil)
|
||||
cdc.RegisterConcrete(cool.QuizMsg{}, "democoin/Quiz", nil)
|
||||
cdc.RegisterConcrete(cool.SetTrendMsg{}, "democoin/SetTrend", nil)
|
||||
cdc.RegisterConcrete(pow.MineMsg{}, "democoin/Mine", nil)
|
||||
cdc.RegisterConcrete(bank.MsgSend{}, "democoin/Send", nil)
|
||||
cdc.RegisterConcrete(bank.MsgIssue{}, "democoin/Issue", nil)
|
||||
cdc.RegisterConcrete(cool.MsgQuiz{}, "democoin/Quiz", nil)
|
||||
cdc.RegisterConcrete(cool.MsgSetTrend{}, "democoin/SetTrend", nil)
|
||||
cdc.RegisterConcrete(pow.MsgMine{}, "democoin/Mine", nil)
|
||||
cdc.RegisterConcrete(ibc.IBCTransferMsg{}, "democoin/IBCTransferMsg", nil)
|
||||
cdc.RegisterConcrete(ibc.IBCReceiveMsg{}, "democoin/IBCReceiveMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.BondMsg{}, "democoin/BondMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.UnbondMsg{}, "democoin/UnbondMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.MsgBond{}, "democoin/BondMsg", nil)
|
||||
cdc.RegisterConcrete(simplestake.MsgUnbond{}, "democoin/UnbondMsg", nil)
|
||||
|
||||
// Register AppAccount
|
||||
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.
|
||||
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
|
||||
return nil, sdk.ErrTxDecode("").Trace(err.Error())
|
||||
}
|
||||
return tx, nil
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keep
|
|||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
}
|
||||
|
||||
err = powKeeper.InitGenesis(ctx, genesisState.PowGenesis)
|
||||
err = powKeeper.InitGenesis(ctx, genesisState.POWGenesis)
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
|
|
|
@ -36,32 +36,32 @@ var (
|
|||
0,
|
||||
}
|
||||
|
||||
sendMsg = bank.SendMsg{
|
||||
sendMsg = bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
|
||||
}
|
||||
|
||||
quizMsg1 = cool.QuizMsg{
|
||||
quizMsg1 = cool.MsgQuiz{
|
||||
Sender: addr1,
|
||||
CoolAnswer: "icecold",
|
||||
}
|
||||
|
||||
quizMsg2 = cool.QuizMsg{
|
||||
quizMsg2 = cool.MsgQuiz{
|
||||
Sender: addr1,
|
||||
CoolAnswer: "badvibesonly",
|
||||
}
|
||||
|
||||
setTrendMsg1 = cool.SetTrendMsg{
|
||||
setTrendMsg1 = cool.MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "icecold",
|
||||
}
|
||||
|
||||
setTrendMsg2 = cool.SetTrendMsg{
|
||||
setTrendMsg2 = cool.MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "badvibesonly",
|
||||
}
|
||||
|
||||
setTrendMsg3 = cool.SetTrendMsg{
|
||||
setTrendMsg3 = cool.MsgSetTrend{
|
||||
Sender: addr1,
|
||||
Cool: "warmandkind",
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ func TestGenesis(t *testing.T) {
|
|||
assert.Equal(t, acc, res1)
|
||||
}
|
||||
|
||||
func TestSendMsgWithAccounts(t *testing.T) {
|
||||
func TestMsgSendWithAccounts(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
// Construct some genesis bytes to reflect democoin/types/AppAccount
|
||||
|
@ -202,12 +202,12 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
|||
|
||||
// Run a Check
|
||||
res := bapp.Check(tx)
|
||||
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
|
||||
// Simulate a Block
|
||||
bapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
|
||||
// Check balances
|
||||
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
|
||||
|
@ -218,22 +218,22 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
|||
|
||||
// Delivering again should cause replay error
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.CodeInvalidSequence, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeInvalidSequence), sdk.ABCICodeType(res.Code), res.Log)
|
||||
|
||||
// bumping the txnonce number without resigning should be an auth error
|
||||
tx.Signatures[0].Sequence = 1
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), sdk.ABCICodeType(res.Code), res.Log)
|
||||
|
||||
// resigning the tx with the bumped sequence should work
|
||||
sequences = []int64{1}
|
||||
sig = priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, tx.Msg))
|
||||
tx.Signatures[0].Signature = sig
|
||||
res = bapp.Deliver(tx)
|
||||
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
assert.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
func TestMineMsg(t *testing.T) {
|
||||
func TestMsgMine(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
// Construct genesis state
|
||||
|
@ -271,11 +271,11 @@ func TestMineMsg(t *testing.T) {
|
|||
assert.Equal(t, acc1, res1)
|
||||
|
||||
// Mine and check for reward
|
||||
mineMsg1 := pow.GenerateMineMsg(addr1, 1, 2)
|
||||
mineMsg1 := pow.GenerateMsgMine(addr1, 1, 2)
|
||||
SignCheckDeliver(t, bapp, mineMsg1, 0, true)
|
||||
CheckBalance(t, bapp, "1pow")
|
||||
// Mine again and check for reward
|
||||
mineMsg2 := pow.GenerateMineMsg(addr1, 2, 3)
|
||||
mineMsg2 := pow.GenerateMsgMine(addr1, 2, 3)
|
||||
SignCheckDeliver(t, bapp, mineMsg2, 1, true)
|
||||
CheckBalance(t, bapp, "2pow")
|
||||
// Mine again - should be invalid
|
||||
|
@ -284,7 +284,7 @@ func TestMineMsg(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestQuizMsg(t *testing.T) {
|
||||
func TestMsgQuiz(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
// Construct genesis state
|
||||
|
@ -403,18 +403,18 @@ func SignCheckDeliver(t *testing.T, bapp *DemocoinApp, msg sdk.Msg, seq int64, e
|
|||
// Run a Check
|
||||
res := bapp.Check(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
// Simulate a Block
|
||||
bapp.BeginBlock(abci.RequestBeginBlock{})
|
||||
res = bapp.Deliver(tx)
|
||||
if expPass {
|
||||
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
} else {
|
||||
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
|
||||
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)
|
||||
}
|
||||
bapp.EndBlock(abci.RequestEndBlock{})
|
||||
//bapp.Commit()
|
||||
|
|
|
@ -45,8 +45,8 @@ func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder {
|
|||
// State to Unmarshal
|
||||
type GenesisState struct {
|
||||
Accounts []*GenesisAccount `json:"accounts"`
|
||||
PowGenesis pow.PowGenesis `json:"pow"`
|
||||
CoolGenesis cool.CoolGenesis `json:"cool"`
|
||||
POWGenesis pow.Genesis `json:"pow"`
|
||||
CoolGenesis cool.Genesis `json:"cool"`
|
||||
}
|
||||
|
||||
// GenesisAccount doesn't need pubkey or sequence
|
||||
|
|
|
@ -34,19 +34,13 @@ func QuizTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
|
||||
// create the message
|
||||
msg := cool.NewQuizMsg(from, args[0])
|
||||
msg := cool.NewMsgQuiz(from, args[0])
|
||||
|
||||
// get account name
|
||||
name := viper.GetString(client.FlagName)
|
||||
|
||||
// default to next sequence number if none provided
|
||||
ctx, err = context.EnsureSequence(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
|
||||
res, err := ctx.EnsureSignBuildBroadcast(name, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -78,17 +72,11 @@ func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
// get account name
|
||||
name := viper.GetString(client.FlagName)
|
||||
|
||||
// default to next sequence number if none provided
|
||||
ctx, err = context.EnsureSequence(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the message
|
||||
msg := cool.NewSetTrendMsg(from, args[0])
|
||||
msg := cool.NewMsgSetTrend(from, args[0])
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
|
||||
res, err := ctx.EnsureSignBuildBroadcast(name, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -6,12 +6,15 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Cool errors reserve 400 ~ 499.
|
||||
const (
|
||||
DefaultCodespace sdk.CodespaceType = 6
|
||||
|
||||
// Cool module reserves error 400-499 lawl
|
||||
CodeIncorrectCoolAnswer sdk.CodeType = 400
|
||||
)
|
||||
|
||||
// ErrIncorrectCoolAnswer - Error returned upon an incorrect guess
|
||||
func ErrIncorrectCoolAnswer(answer string) sdk.Error {
|
||||
return sdk.NewError(CodeIncorrectCoolAnswer, fmt.Sprintf("Incorrect cool answer: %v", answer))
|
||||
func ErrIncorrectCoolAnswer(codespace sdk.CodespaceType, answer string) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeIncorrectCoolAnswer, fmt.Sprintf("Incorrect cool answer: %v", answer))
|
||||
}
|
||||
|
|
|
@ -21,10 +21,10 @@ import (
|
|||
func NewHandler(k Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case SetTrendMsg:
|
||||
return handleSetTrendMsg(ctx, k, msg)
|
||||
case QuizMsg:
|
||||
return handleQuizMsg(ctx, k, msg)
|
||||
case MsgSetTrend:
|
||||
return handleMsgSetTrend(ctx, k, msg)
|
||||
case MsgQuiz:
|
||||
return handleMsgQuiz(ctx, k, msg)
|
||||
default:
|
||||
errMsg := fmt.Sprintf("Unrecognized cool Msg type: %v", reflect.TypeOf(msg).Name())
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
|
@ -32,19 +32,19 @@ func NewHandler(k Keeper) sdk.Handler {
|
|||
}
|
||||
}
|
||||
|
||||
// Handle QuizMsg This is the engine of your module
|
||||
func handleSetTrendMsg(ctx sdk.Context, k Keeper, msg SetTrendMsg) sdk.Result {
|
||||
// Handle MsgQuiz This is the engine of your module
|
||||
func handleMsgSetTrend(ctx sdk.Context, k Keeper, msg MsgSetTrend) sdk.Result {
|
||||
k.setTrend(ctx, msg.Cool)
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
// Handle QuizMsg This is the engine of your module
|
||||
func handleQuizMsg(ctx sdk.Context, k Keeper, msg QuizMsg) sdk.Result {
|
||||
// Handle MsgQuiz This is the engine of your module
|
||||
func handleMsgQuiz(ctx sdk.Context, k Keeper, msg MsgQuiz) sdk.Result {
|
||||
|
||||
correct := k.CheckTrend(ctx, msg.CoolAnswer)
|
||||
|
||||
if !correct {
|
||||
return ErrIncorrectCoolAnswer(msg.CoolAnswer).Result()
|
||||
return ErrIncorrectCoolAnswer(k.codespace, msg.CoolAnswer).Result()
|
||||
}
|
||||
|
||||
if ctx.IsCheckTx() {
|
||||
|
|
|
@ -7,14 +7,16 @@ import (
|
|||
|
||||
// Keeper - handlers sets/gets of custom variables for your module
|
||||
type Keeper struct {
|
||||
ck bank.CoinKeeper
|
||||
ck bank.Keeper
|
||||
|
||||
storeKey sdk.StoreKey // The (unexposed) key used to access the store from the Context.
|
||||
|
||||
codespace sdk.CodespaceType
|
||||
}
|
||||
|
||||
// NewKeeper - Returns the Keeper
|
||||
func NewKeeper(key sdk.StoreKey, bankKeeper bank.CoinKeeper) Keeper {
|
||||
return Keeper{bankKeeper, key}
|
||||
func NewKeeper(key sdk.StoreKey, bankKeeper bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
||||
return Keeper{bankKeeper, key, codespace}
|
||||
}
|
||||
|
||||
// Key to knowing the trend on the streets!
|
||||
|
@ -42,7 +44,7 @@ func (k Keeper) CheckTrend(ctx sdk.Context, guessedTrend string) bool {
|
|||
}
|
||||
|
||||
// InitGenesis - store the genesis trend
|
||||
func (k Keeper) InitGenesis(ctx sdk.Context, data CoolGenesis) error {
|
||||
func (k Keeper) InitGenesis(ctx sdk.Context, data Genesis) error {
|
||||
k.setTrend(ctx, data.Trend)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -8,39 +8,39 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// A really cool msg type, these fields are can be entirely arbitrary and
|
||||
// a really cool msg type, these fields are can be entirely arbitrary and
|
||||
// custom to your message
|
||||
type SetTrendMsg struct {
|
||||
type MsgSetTrend struct {
|
||||
Sender sdk.Address
|
||||
Cool string
|
||||
}
|
||||
|
||||
// Genesis state - specify genesis trend
|
||||
type CoolGenesis struct {
|
||||
// genesis state - specify genesis trend
|
||||
type Genesis struct {
|
||||
Trend string `json:"trend"`
|
||||
}
|
||||
|
||||
// New cool message
|
||||
func NewSetTrendMsg(sender sdk.Address, cool string) SetTrendMsg {
|
||||
return SetTrendMsg{
|
||||
// new cool message
|
||||
func NewMsgSetTrend(sender sdk.Address, cool string) MsgSetTrend {
|
||||
return MsgSetTrend{
|
||||
Sender: sender,
|
||||
Cool: cool,
|
||||
}
|
||||
}
|
||||
|
||||
// enforce the msg type at compile time
|
||||
var _ sdk.Msg = SetTrendMsg{}
|
||||
var _ sdk.Msg = MsgSetTrend{}
|
||||
|
||||
// nolint
|
||||
func (msg SetTrendMsg) Type() string { return "cool" }
|
||||
func (msg SetTrendMsg) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg SetTrendMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg SetTrendMsg) String() string {
|
||||
return fmt.Sprintf("SetTrendMsg{Sender: %v, Cool: %v}", msg.Sender, msg.Cool)
|
||||
func (msg MsgSetTrend) Type() string { return "cool" }
|
||||
func (msg MsgSetTrend) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg MsgSetTrend) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg MsgSetTrend) String() string {
|
||||
return fmt.Sprintf("MsgSetTrend{Sender: %v, Cool: %v}", msg.Sender, msg.Cool)
|
||||
}
|
||||
|
||||
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
|
||||
func (msg SetTrendMsg) ValidateBasic() sdk.Error {
|
||||
func (msg MsgSetTrend) ValidateBasic() sdk.Error {
|
||||
if len(msg.Sender) == 0 {
|
||||
return sdk.ErrUnknownAddress(msg.Sender.String()).Trace("")
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ func (msg SetTrendMsg) ValidateBasic() sdk.Error {
|
|||
}
|
||||
|
||||
// Get the bytes for the message signer to sign on
|
||||
func (msg SetTrendMsg) GetSignBytes() []byte {
|
||||
func (msg MsgSetTrend) GetSignBytes() []byte {
|
||||
b, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -66,32 +66,32 @@ func (msg SetTrendMsg) GetSignBytes() []byte {
|
|||
|
||||
// A message type to quiz how cool you are. these fields are can be entirely
|
||||
// arbitrary and custom to your message
|
||||
type QuizMsg struct {
|
||||
type MsgQuiz struct {
|
||||
Sender sdk.Address
|
||||
CoolAnswer string
|
||||
}
|
||||
|
||||
// New cool message
|
||||
func NewQuizMsg(sender sdk.Address, coolerthancool string) QuizMsg {
|
||||
return QuizMsg{
|
||||
func NewMsgQuiz(sender sdk.Address, coolerthancool string) MsgQuiz {
|
||||
return MsgQuiz{
|
||||
Sender: sender,
|
||||
CoolAnswer: coolerthancool,
|
||||
}
|
||||
}
|
||||
|
||||
// enforce the msg type at compile time
|
||||
var _ sdk.Msg = QuizMsg{}
|
||||
var _ sdk.Msg = MsgQuiz{}
|
||||
|
||||
// nolint
|
||||
func (msg QuizMsg) Type() string { return "cool" }
|
||||
func (msg QuizMsg) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg QuizMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg QuizMsg) String() string {
|
||||
return fmt.Sprintf("QuizMsg{Sender: %v, CoolAnswer: %v}", msg.Sender, msg.CoolAnswer)
|
||||
func (msg MsgQuiz) Type() string { return "cool" }
|
||||
func (msg MsgQuiz) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg MsgQuiz) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg MsgQuiz) String() string {
|
||||
return fmt.Sprintf("MsgQuiz{Sender: %v, CoolAnswer: %v}", msg.Sender, msg.CoolAnswer)
|
||||
}
|
||||
|
||||
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
|
||||
func (msg QuizMsg) ValidateBasic() sdk.Error {
|
||||
func (msg MsgQuiz) ValidateBasic() sdk.Error {
|
||||
if len(msg.Sender) == 0 {
|
||||
return sdk.ErrUnknownAddress(msg.Sender.String()).Trace("")
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ func (msg QuizMsg) ValidateBasic() sdk.Error {
|
|||
}
|
||||
|
||||
// Get the bytes for the message signer to sign on
|
||||
func (msg QuizMsg) GetSignBytes() []byte {
|
||||
func (msg MsgQuiz) GetSignBytes() []byte {
|
||||
b, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||
)
|
||||
|
||||
// command to mine some pow!
|
||||
func MineCmd(cdc *wire.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "mine [difficulty] [count] [nonce] [solution]",
|
||||
|
@ -36,12 +37,10 @@ func MineCmd(cdc *wire.Codec) *cobra.Command {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
count, err := strconv.ParseUint(args[1], 0, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nonce, err := strconv.ParseUint(args[2], 0, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -49,19 +48,13 @@ func MineCmd(cdc *wire.Codec) *cobra.Command {
|
|||
|
||||
solution := []byte(args[3])
|
||||
|
||||
msg := pow.NewMineMsg(from, difficulty, count, nonce, solution)
|
||||
msg := pow.NewMsgMine(from, difficulty, count, nonce, solution)
|
||||
|
||||
// get account name
|
||||
name := ctx.FromAddressName
|
||||
|
||||
// default to next sequence number if none provided
|
||||
ctx, err = context.EnsureSequence(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
|
||||
res, err := ctx.EnsureSignBuildBroadcast(name, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -4,17 +4,20 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// TODO remove, seems hacky
|
||||
type CodeType = sdk.CodeType
|
||||
|
||||
// POW errors reserve 200 ~ 299
|
||||
const (
|
||||
CodeInvalidDifficulty CodeType = 201
|
||||
CodeNonexistentDifficulty CodeType = 202
|
||||
CodeNonexistentReward CodeType = 203
|
||||
CodeNonexistentCount CodeType = 204
|
||||
CodeInvalidProof CodeType = 205
|
||||
CodeNotBelowTarget CodeType = 206
|
||||
CodeInvalidCount CodeType = 207
|
||||
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
|
||||
DefaultCodespace sdk.CodespaceType = 5
|
||||
CodeInvalidDifficulty CodeType = 201
|
||||
CodeNonexistentDifficulty CodeType = 202
|
||||
CodeNonexistentReward CodeType = 203
|
||||
CodeNonexistentCount CodeType = 204
|
||||
CodeInvalidProof CodeType = 205
|
||||
CodeNotBelowTarget CodeType = 206
|
||||
CodeInvalidCount CodeType = 207
|
||||
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
|
||||
)
|
||||
|
||||
func codeToDefaultMsg(code CodeType) string {
|
||||
|
@ -40,43 +43,37 @@ func codeToDefaultMsg(code CodeType) string {
|
|||
}
|
||||
}
|
||||
|
||||
func ErrInvalidDifficulty(msg string) sdk.Error {
|
||||
return newError(CodeInvalidDifficulty, msg)
|
||||
// nolint
|
||||
func ErrInvalidDifficulty(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||
return newError(codespace, CodeInvalidDifficulty, msg)
|
||||
}
|
||||
|
||||
func ErrNonexistentDifficulty() sdk.Error {
|
||||
return newError(CodeNonexistentDifficulty, "")
|
||||
func ErrNonexistentDifficulty(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeNonexistentDifficulty, "")
|
||||
}
|
||||
|
||||
func ErrNonexistentReward() sdk.Error {
|
||||
return newError(CodeNonexistentReward, "")
|
||||
func ErrNonexistentReward(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeNonexistentReward, "")
|
||||
}
|
||||
|
||||
func ErrNonexistentCount() sdk.Error {
|
||||
return newError(CodeNonexistentCount, "")
|
||||
func ErrNonexistentCount(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeNonexistentCount, "")
|
||||
}
|
||||
|
||||
func ErrInvalidProof(msg string) sdk.Error {
|
||||
return newError(CodeInvalidProof, msg)
|
||||
func ErrInvalidProof(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||
return newError(codespace, CodeInvalidProof, msg)
|
||||
}
|
||||
|
||||
func ErrNotBelowTarget(msg string) sdk.Error {
|
||||
return newError(CodeNotBelowTarget, msg)
|
||||
func ErrNotBelowTarget(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||
return newError(codespace, CodeNotBelowTarget, msg)
|
||||
}
|
||||
|
||||
func ErrInvalidCount(msg string) sdk.Error {
|
||||
return newError(CodeInvalidCount, msg)
|
||||
func ErrInvalidCount(codespace sdk.CodespaceType, msg string) sdk.Error {
|
||||
return newError(codespace, CodeInvalidCount, msg)
|
||||
}
|
||||
|
||||
func msgOrDefaultMsg(msg string, code CodeType) string {
|
||||
if msg != "" {
|
||||
return msg
|
||||
} else {
|
||||
return codeToDefaultMsg(code)
|
||||
}
|
||||
return codeToDefaultMsg(code)
|
||||
}
|
||||
|
||||
func newError(code CodeType, msg string) sdk.Error {
|
||||
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
|
||||
msg = msgOrDefaultMsg(msg, code)
|
||||
return sdk.NewError(code, msg)
|
||||
return sdk.NewError(codespace, code, msg)
|
||||
}
|
||||
|
|
|
@ -6,17 +6,18 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// POW handler
|
||||
func (pk Keeper) Handler(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case MineMsg:
|
||||
return handleMineMsg(ctx, pk, msg)
|
||||
case MsgMine:
|
||||
return handleMsgMine(ctx, pk, msg)
|
||||
default:
|
||||
errMsg := "Unrecognized pow Msg type: " + reflect.TypeOf(msg).Name()
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
}
|
||||
}
|
||||
|
||||
func handleMineMsg(ctx sdk.Context, pk Keeper, msg MineMsg) sdk.Result {
|
||||
func handleMsgMine(ctx sdk.Context, pk Keeper, msg MsgMine) sdk.Result {
|
||||
|
||||
// precondition: msg has passed ValidateBasic
|
||||
|
||||
|
|
|
@ -20,9 +20,9 @@ func TestPowHandler(t *testing.T) {
|
|||
|
||||
am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{})
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
||||
config := NewPowConfig("pow", int64(1))
|
||||
ck := bank.NewCoinKeeper(am)
|
||||
keeper := NewKeeper(capKey, config, ck)
|
||||
config := NewConfig("pow", int64(1))
|
||||
ck := bank.NewKeeper(am)
|
||||
keeper := NewKeeper(capKey, config, ck, DefaultCodespace)
|
||||
|
||||
handler := keeper.Handler
|
||||
|
||||
|
@ -30,11 +30,11 @@ func TestPowHandler(t *testing.T) {
|
|||
count := uint64(1)
|
||||
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)
|
||||
|
||||
nonce, proof := mine(addr, count, difficulty)
|
||||
msg := NewMineMsg(addr, difficulty, count, nonce, proof)
|
||||
msg := NewMsgMine(addr, difficulty, count, nonce, proof)
|
||||
|
||||
result := handler(ctx, msg)
|
||||
assert.Equal(t, result, sdk.Result{})
|
||||
|
@ -51,7 +51,7 @@ func TestPowHandler(t *testing.T) {
|
|||
|
||||
difficulty = uint64(4)
|
||||
nonce, proof = mine(addr, count, difficulty)
|
||||
msg = NewMineMsg(addr, difficulty, count, nonce, proof)
|
||||
msg = NewMsgMine(addr, difficulty, count, nonce, proof)
|
||||
|
||||
result = handler(ctx, msg)
|
||||
assert.NotEqual(t, result, sdk.Result{})
|
||||
|
|
|
@ -9,41 +9,45 @@ import (
|
|||
)
|
||||
|
||||
// module users must specify coin denomination and reward (constant) per PoW solution
|
||||
type PowConfig struct {
|
||||
type Config struct {
|
||||
Denomination string
|
||||
Reward int64
|
||||
}
|
||||
|
||||
// genesis info must specify starting difficulty and starting count
|
||||
type PowGenesis struct {
|
||||
type Genesis struct {
|
||||
Difficulty uint64 `json:"difficulty"`
|
||||
Count uint64 `json:"count"`
|
||||
}
|
||||
|
||||
// POW Keeper
|
||||
type Keeper struct {
|
||||
key sdk.StoreKey
|
||||
config PowConfig
|
||||
ck bank.CoinKeeper
|
||||
key sdk.StoreKey
|
||||
config Config
|
||||
ck bank.Keeper
|
||||
codespace sdk.CodespaceType
|
||||
}
|
||||
|
||||
func NewPowConfig(denomination string, reward int64) PowConfig {
|
||||
return PowConfig{denomination, reward}
|
||||
func NewConfig(denomination string, reward int64) Config {
|
||||
return Config{denomination, reward}
|
||||
}
|
||||
|
||||
func NewKeeper(key sdk.StoreKey, config PowConfig, ck bank.CoinKeeper) Keeper {
|
||||
return Keeper{key, config, ck}
|
||||
func NewKeeper(key sdk.StoreKey, config Config, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
||||
return Keeper{key, config, ck, codespace}
|
||||
}
|
||||
|
||||
func (pk Keeper) InitGenesis(ctx sdk.Context, genesis PowGenesis) error {
|
||||
pk.SetLastDifficulty(ctx, genesis.Difficulty)
|
||||
pk.SetLastCount(ctx, genesis.Count)
|
||||
// Init Genessis for the POW module
|
||||
func (k Keeper) InitGenesis(ctx sdk.Context, genesis Genesis) error {
|
||||
k.SetLastDifficulty(ctx, genesis.Difficulty)
|
||||
k.SetLastCount(ctx, genesis.Count)
|
||||
return nil
|
||||
}
|
||||
|
||||
var lastDifficultyKey = []byte("lastDifficultyKey")
|
||||
|
||||
func (pk Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
// get the last mining difficulty
|
||||
func (k Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(k.key)
|
||||
stored := store.Get(lastDifficultyKey)
|
||||
if stored == nil {
|
||||
panic("no stored difficulty")
|
||||
|
@ -52,15 +56,17 @@ func (pk Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (pk Keeper) SetLastDifficulty(ctx sdk.Context, diff uint64) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
// set the last mining difficulty
|
||||
func (k Keeper) SetLastDifficulty(ctx sdk.Context, diff uint64) {
|
||||
store := ctx.KVStore(k.key)
|
||||
store.Set(lastDifficultyKey, []byte(strconv.FormatUint(diff, 16)))
|
||||
}
|
||||
|
||||
var countKey = []byte("count")
|
||||
|
||||
func (pk Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
// get the last count
|
||||
func (k Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
|
||||
store := ctx.KVStore(k.key)
|
||||
stored := store.Get(countKey)
|
||||
if stored == nil {
|
||||
panic("no stored count")
|
||||
|
@ -69,45 +75,45 @@ func (pk Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (pk Keeper) SetLastCount(ctx sdk.Context, count uint64) {
|
||||
store := ctx.KVStore(pk.key)
|
||||
// set the last count
|
||||
func (k Keeper) SetLastCount(ctx sdk.Context, count uint64) {
|
||||
store := ctx.KVStore(k.key)
|
||||
store.Set(countKey, []byte(strconv.FormatUint(count, 16)))
|
||||
}
|
||||
|
||||
func (pk Keeper) CheckValid(ctx sdk.Context, difficulty uint64, count uint64) (uint64, uint64, sdk.Error) {
|
||||
// Is the keeper state valid?
|
||||
func (k Keeper) CheckValid(ctx sdk.Context, difficulty uint64, count uint64) (uint64, uint64, sdk.Error) {
|
||||
|
||||
lastDifficulty, err := pk.GetLastDifficulty(ctx)
|
||||
lastDifficulty, err := k.GetLastDifficulty(ctx)
|
||||
if err != nil {
|
||||
return 0, 0, ErrNonexistentDifficulty()
|
||||
return 0, 0, ErrNonexistentDifficulty(k.codespace)
|
||||
}
|
||||
|
||||
newDifficulty := lastDifficulty + 1
|
||||
|
||||
lastCount, err := pk.GetLastCount(ctx)
|
||||
lastCount, err := k.GetLastCount(ctx)
|
||||
if err != nil {
|
||||
return 0, 0, ErrNonexistentCount()
|
||||
return 0, 0, ErrNonexistentCount(k.codespace)
|
||||
}
|
||||
|
||||
newCount := lastCount + 1
|
||||
|
||||
if count != newCount {
|
||||
return 0, 0, ErrInvalidCount(fmt.Sprintf("invalid count: was %d, should have been %d", count, newCount))
|
||||
return 0, 0, ErrInvalidCount(k.codespace, fmt.Sprintf("invalid count: was %d, should have been %d", count, newCount))
|
||||
}
|
||||
|
||||
if difficulty != newDifficulty {
|
||||
return 0, 0, ErrInvalidDifficulty(fmt.Sprintf("invalid difficulty: was %d, should have been %d", difficulty, newDifficulty))
|
||||
return 0, 0, ErrInvalidDifficulty(k.codespace, fmt.Sprintf("invalid difficulty: was %d, should have been %d", difficulty, newDifficulty))
|
||||
}
|
||||
|
||||
return newDifficulty, newCount, nil
|
||||
|
||||
}
|
||||
|
||||
func (pk Keeper) ApplyValid(ctx sdk.Context, sender sdk.Address, newDifficulty uint64, newCount uint64) sdk.Error {
|
||||
_, ckErr := pk.ck.AddCoins(ctx, sender, []sdk.Coin{sdk.Coin{pk.config.Denomination, pk.config.Reward}})
|
||||
// Add some coins for a POW well done
|
||||
func (k Keeper) ApplyValid(ctx sdk.Context, sender sdk.Address, newDifficulty uint64, newCount uint64) sdk.Error {
|
||||
_, ckErr := k.ck.AddCoins(ctx, sender, []sdk.Coin{sdk.Coin{k.config.Denomination, k.config.Reward}})
|
||||
if ckErr != nil {
|
||||
return ckErr
|
||||
}
|
||||
pk.SetLastDifficulty(ctx, newDifficulty)
|
||||
pk.SetLastCount(ctx, newCount)
|
||||
k.SetLastDifficulty(ctx, newDifficulty)
|
||||
k.SetLastCount(ctx, newCount)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -33,11 +33,11 @@ func TestPowKeeperGetSet(t *testing.T) {
|
|||
|
||||
am := auth.NewAccountMapper(cdc, capKey, &auth.BaseAccount{})
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
||||
config := NewPowConfig("pow", int64(1))
|
||||
ck := bank.NewCoinKeeper(am)
|
||||
keeper := NewKeeper(capKey, config, ck)
|
||||
config := NewConfig("pow", int64(1))
|
||||
ck := bank.NewKeeper(am)
|
||||
keeper := NewKeeper(capKey, config, ck, DefaultCodespace)
|
||||
|
||||
err := keeper.InitGenesis(ctx, PowGenesis{uint64(1), uint64(0)})
|
||||
err := keeper.InitGenesis(ctx, Genesis{uint64(1), uint64(0)})
|
||||
assert.Nil(t, err)
|
||||
|
||||
res, err := keeper.GetLastDifficulty(ctx)
|
||||
|
|
|
@ -9,9 +9,10 @@ import (
|
|||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
func GenerateMineMsg(sender sdk.Address, count uint64, difficulty uint64) MineMsg {
|
||||
// generate the mine message
|
||||
func GenerateMsgMine(sender sdk.Address, count uint64, difficulty uint64) MsgMine {
|
||||
nonce, hash := mine(sender, count, difficulty)
|
||||
return NewMineMsg(sender, difficulty, count, nonce, hash)
|
||||
return NewMsgMine(sender, difficulty, count, nonce, hash)
|
||||
}
|
||||
|
||||
func hash(sender sdk.Address, count uint64, nonce uint64) []byte {
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// MineMsg - mine some coins with PoW
|
||||
type MineMsg struct {
|
||||
// MsgMine - mine some coins with PoW
|
||||
type MsgMine struct {
|
||||
Sender sdk.Address `json:"sender"`
|
||||
Difficulty uint64 `json:"difficulty"`
|
||||
Count uint64 `json:"count"`
|
||||
|
@ -23,21 +23,23 @@ type MineMsg struct {
|
|||
}
|
||||
|
||||
// enforce the msg type at compile time
|
||||
var _ sdk.Msg = MineMsg{}
|
||||
var _ sdk.Msg = MsgMine{}
|
||||
|
||||
// NewMineMsg - construct mine message
|
||||
func NewMineMsg(sender sdk.Address, difficulty uint64, count uint64, nonce uint64, proof []byte) MineMsg {
|
||||
return MineMsg{sender, difficulty, count, nonce, proof}
|
||||
// NewMsgMine - construct mine message
|
||||
func NewMsgMine(sender sdk.Address, difficulty uint64, count uint64, nonce uint64, proof []byte) MsgMine {
|
||||
return MsgMine{sender, difficulty, count, nonce, proof}
|
||||
}
|
||||
|
||||
func (msg MineMsg) Type() string { return "pow" }
|
||||
func (msg MineMsg) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg MineMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg MineMsg) String() string {
|
||||
return fmt.Sprintf("MineMsg{Sender: %v, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
|
||||
// nolint
|
||||
func (msg MsgMine) Type() string { return "pow" }
|
||||
func (msg MsgMine) Get(key interface{}) (value interface{}) { return nil }
|
||||
func (msg MsgMine) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
|
||||
func (msg MsgMine) String() string {
|
||||
return fmt.Sprintf("MsgMine{Sender: %v, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
|
||||
}
|
||||
|
||||
func (msg MineMsg) ValidateBasic() sdk.Error {
|
||||
// validate the mine message
|
||||
func (msg MsgMine) ValidateBasic() sdk.Error {
|
||||
// check hash
|
||||
var data []byte
|
||||
// hash must include sender, so no other users can race the tx
|
||||
|
@ -52,7 +54,7 @@ func (msg MineMsg) ValidateBasic() sdk.Error {
|
|||
hex.Encode(hashHex, hash)
|
||||
hashHex = hashHex[:16]
|
||||
if !bytes.Equal(hashHex, msg.Proof) {
|
||||
return ErrInvalidProof(fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
|
||||
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
|
||||
}
|
||||
|
||||
// check proof below difficulty
|
||||
|
@ -60,16 +62,17 @@ func (msg MineMsg) ValidateBasic() sdk.Error {
|
|||
target := math.MaxUint64 / msg.Difficulty
|
||||
hashUint, err := strconv.ParseUint(string(msg.Proof), 16, 64)
|
||||
if err != nil {
|
||||
return ErrInvalidProof(fmt.Sprintf("proof: %s", msg.Proof))
|
||||
return ErrInvalidProof(DefaultCodespace, fmt.Sprintf("proof: %s", msg.Proof))
|
||||
}
|
||||
if hashUint >= target {
|
||||
return ErrNotBelowTarget(fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
|
||||
return ErrNotBelowTarget(DefaultCodespace, fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg MineMsg) GetSignBytes() []byte {
|
||||
// get the mine message sign bytes
|
||||
func (msg MsgMine) GetSignBytes() []byte {
|
||||
b, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
|
@ -9,70 +9,72 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestNewMineMsg(t *testing.T) {
|
||||
func TestNewMsgMine(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 0, 0, 0, []byte("")}
|
||||
equiv := NewMineMsg(addr, 0, 0, 0, []byte(""))
|
||||
msg := MsgMine{addr, 0, 0, 0, []byte("")}
|
||||
equiv := NewMsgMine(addr, 0, 0, 0, []byte(""))
|
||||
assert.Equal(t, msg, equiv, "%s != %s", msg, equiv)
|
||||
}
|
||||
|
||||
func TestMineMsgType(t *testing.T) {
|
||||
func TestMsgMineType(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 0, 0, 0, []byte("")}
|
||||
msg := MsgMine{addr, 0, 0, 0, []byte("")}
|
||||
assert.Equal(t, msg.Type(), "pow")
|
||||
}
|
||||
|
||||
func TestMineMsgValidation(t *testing.T) {
|
||||
func TestMsgMineValidation(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
otherAddr := sdk.Address([]byte("another"))
|
||||
count := uint64(0)
|
||||
|
||||
for difficulty := uint64(1); difficulty < 1000; difficulty += 100 {
|
||||
count += 1
|
||||
|
||||
count++
|
||||
nonce, proof := mine(addr, count, difficulty)
|
||||
msg := MineMsg{addr, difficulty, count, nonce, proof}
|
||||
msg := MsgMine{addr, difficulty, count, nonce, proof}
|
||||
err := msg.ValidateBasic()
|
||||
assert.Nil(t, err, "error with difficulty %d - %+v", difficulty, err)
|
||||
|
||||
msg.Count += 1
|
||||
msg.Count++
|
||||
err = msg.ValidateBasic()
|
||||
assert.NotNil(t, err, "count was wrong, should have thrown error with msg %s", msg)
|
||||
|
||||
msg.Count -= 1
|
||||
msg.Nonce += 1
|
||||
msg.Count--
|
||||
msg.Nonce++
|
||||
err = msg.ValidateBasic()
|
||||
assert.NotNil(t, err, "nonce was wrong, should have thrown error with msg %s", msg)
|
||||
|
||||
msg.Nonce -= 1
|
||||
msg.Nonce--
|
||||
msg.Sender = otherAddr
|
||||
err = msg.ValidateBasic()
|
||||
assert.NotNil(t, err, "sender was wrong, should have thrown error with msg %s", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMineMsgString(t *testing.T) {
|
||||
func TestMsgMineString(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 0, 0, 0, []byte("abc")}
|
||||
msg := MsgMine{addr, 0, 0, 0, []byte("abc")}
|
||||
res := msg.String()
|
||||
assert.Equal(t, res, "MineMsg{Sender: 73656E646572, Difficulty: 0, Count: 0, Nonce: 0, Proof: abc}")
|
||||
assert.Equal(t, res, "MsgMine{Sender: 73656E646572, Difficulty: 0, Count: 0, Nonce: 0, Proof: abc}")
|
||||
}
|
||||
|
||||
func TestMineMsgGet(t *testing.T) {
|
||||
func TestMsgMineGet(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 0, 0, 0, []byte("")}
|
||||
msg := MsgMine{addr, 0, 0, 0, []byte("")}
|
||||
res := msg.Get(nil)
|
||||
assert.Nil(t, res)
|
||||
}
|
||||
|
||||
func TestMineMsgGetSignBytes(t *testing.T) {
|
||||
func TestMsgMineGetSignBytes(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 1, 1, 1, []byte("abc")}
|
||||
msg := MsgMine{addr, 1, 1, 1, []byte("abc")}
|
||||
res := msg.GetSignBytes()
|
||||
assert.Equal(t, string(res), `{"sender":"73656E646572","difficulty":1,"count":1,"nonce":1,"proof":"YWJj"}`)
|
||||
}
|
||||
|
||||
func TestMineMsgGetSigners(t *testing.T) {
|
||||
func TestMsgMineGetSigners(t *testing.T) {
|
||||
addr := sdk.Address([]byte("sender"))
|
||||
msg := MineMsg{addr, 1, 1, 1, []byte("abc")}
|
||||
msg := MsgMine{addr, 1, 1, 1, []byte("abc")}
|
||||
res := msg.GetSigners()
|
||||
assert.Equal(t, fmt.Sprintf("%v", res), "[73656E646572]")
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func main() {
|
|||
baseApp.SetTxDecoder(decodeTx)
|
||||
|
||||
// Set a handler Route.
|
||||
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore))
|
||||
baseApp.Router().AddRoute("kvstore", Handler(capKeyMainStore))
|
||||
|
||||
// Load latest version.
|
||||
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
|
||||
|
@ -65,11 +65,12 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
func KVStoreHandler(storeKey sdk.StoreKey) sdk.Handler {
|
||||
// KVStore Handler
|
||||
func Handler(storeKey sdk.StoreKey) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
dTx, ok := msg.(kvstoreTx)
|
||||
if !ok {
|
||||
panic("KVStoreHandler should only receive kvstoreTx")
|
||||
panic("Handler should only receive kvstoreTx")
|
||||
}
|
||||
|
||||
// tx is already unmarshalled
|
||||
|
|
|
@ -70,16 +70,16 @@ func (c initCmd) run(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// generate secrete and address
|
||||
// generate secret and address
|
||||
addr, secret, err := GenerateCoinKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var DEFAULT_DENOM = "mycoin"
|
||||
var defaultDenom = "mycoin"
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
|
@ -12,73 +15,59 @@ import (
|
|||
|
||||
// ShowNodeIDCmd - ported from Tendermint, dump node ID to stdout
|
||||
func ShowNodeIDCmd(ctx *Context) *cobra.Command {
|
||||
cmd := showNodeId{ctx}
|
||||
return &cobra.Command{
|
||||
Use: "show_node_id",
|
||||
Short: "Show this node's ID",
|
||||
RunE: cmd.run,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg := ctx.Config
|
||||
nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(nodeKey.ID())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(nodeKey.ID())
|
||||
return nil
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
// ShowValidator - ported from Tendermint, show this node's validator info
|
||||
func ShowValidatorCmd(ctx *Context) *cobra.Command {
|
||||
cmd := showValidator{ctx}
|
||||
return &cobra.Command{
|
||||
flagJSON := "json"
|
||||
cmd := cobra.Command{
|
||||
Use: "show_validator",
|
||||
Short: "Show this node's validator info",
|
||||
RunE: cmd.run,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
cfg := ctx.Config
|
||||
privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorFile())
|
||||
pubKey := privValidator.PubKey
|
||||
|
||||
if viper.GetBool(flagJSON) {
|
||||
pubKeyJSONBytes, err := cdc.MarshalJSON(pubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(pubKeyJSONBytes))
|
||||
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
|
||||
}
|
||||
|
||||
type showValidator struct {
|
||||
context *Context
|
||||
}
|
||||
|
||||
func (s showValidator) run(cmd *cobra.Command, args []string) error {
|
||||
cfg := s.context.Config
|
||||
privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorFile())
|
||||
pubKeyJSONBytes, err := cdc.MarshalJSON(privValidator.PubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(pubKeyJSONBytes))
|
||||
return nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// UnsafeResetAllCmd - extension of the tendermint command, resets initialization
|
||||
func UnsafeResetAllCmd(ctx *Context) *cobra.Command {
|
||||
cmd := resetAll{ctx}
|
||||
return &cobra.Command{
|
||||
Use: "unsafe_reset_all",
|
||||
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)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
||||
// server context
|
||||
type Context struct {
|
||||
Config *cfg.Config
|
||||
Logger log.Logger
|
||||
|
@ -59,6 +60,7 @@ func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error
|
|||
}
|
||||
}
|
||||
|
||||
// add server commands
|
||||
func AddCommands(
|
||||
rootCmd *cobra.Command,
|
||||
appState GenAppState, appCreator AppCreator,
|
||||
|
|
|
@ -26,13 +26,12 @@ type cacheKVStore struct {
|
|||
|
||||
var _ CacheKVStore = (*cacheKVStore)(nil)
|
||||
|
||||
// nolint
|
||||
func NewCacheKVStore(parent KVStore) *cacheKVStore {
|
||||
|
||||
ci := &cacheKVStore{
|
||||
cache: make(map[string]cValue),
|
||||
parent: parent,
|
||||
}
|
||||
|
||||
return ci
|
||||
}
|
||||
|
||||
|
@ -170,9 +169,8 @@ func (ci *cacheKVStore) dirtyItems(ascending bool) []cmn.KVPair {
|
|||
sort.Slice(items, func(i, j int) bool {
|
||||
if ascending {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ func TestCacheKVIteratorBounds(t *testing.T) {
|
|||
k, v := itr.Key(), itr.Value()
|
||||
assert.Equal(t, keyFmt(i), k)
|
||||
assert.Equal(t, valFmt(i), v)
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, nItems, i)
|
||||
|
||||
|
@ -113,7 +113,7 @@ func TestCacheKVIteratorBounds(t *testing.T) {
|
|||
itr = st.Iterator(bz("money"), nil)
|
||||
i = 0
|
||||
for ; itr.Valid(); itr.Next() {
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, 0, i)
|
||||
|
||||
|
@ -124,7 +124,7 @@ func TestCacheKVIteratorBounds(t *testing.T) {
|
|||
k, v := itr.Key(), itr.Value()
|
||||
assert.Equal(t, keyFmt(i), k)
|
||||
assert.Equal(t, valFmt(i), v)
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, 3, i)
|
||||
|
||||
|
@ -135,7 +135,7 @@ func TestCacheKVIteratorBounds(t *testing.T) {
|
|||
k, v := itr.Key(), itr.Value()
|
||||
assert.Equal(t, keyFmt(i), k)
|
||||
assert.Equal(t, valFmt(i), v)
|
||||
i += 1
|
||||
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()
|
||||
assert.Equal(t, keyFmt(i), k)
|
||||
assert.Equal(t, valFmt(i), v)
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, expectedN, i)
|
||||
}
|
||||
|
@ -397,7 +397,7 @@ func assertIterateDomainCheck(t *testing.T, st KVStore, mem dbm.DB, r []keyRange
|
|||
|
||||
itr.Next()
|
||||
itr2.Next()
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
|
||||
assert.False(t, itr.Valid())
|
||||
|
@ -479,10 +479,10 @@ func (krc *keyRangeCounter) valid() bool {
|
|||
func (krc *keyRangeCounter) next() {
|
||||
thisKeyRange := krc.keyRanges[krc.rangeIdx]
|
||||
if krc.idx == thisKeyRange.len()-1 {
|
||||
krc.rangeIdx += 1
|
||||
krc.rangeIdx++
|
||||
krc.idx = 0
|
||||
} else {
|
||||
krc.idx += 1
|
||||
krc.idx++
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -151,9 +151,8 @@ func (iter *cacheMergeIterator) Close() {
|
|||
func (iter *cacheMergeIterator) compare(a, b []byte) int {
|
||||
if iter.ascending {
|
||||
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,
|
||||
|
|
|
@ -10,7 +10,7 @@ type dbStoreAdapter struct {
|
|||
}
|
||||
|
||||
// Implements Store.
|
||||
func (_ dbStoreAdapter) GetStoreType() StoreType {
|
||||
func (dbStoreAdapter) GetStoreType() StoreType {
|
||||
return sdk.StoreTypeDB
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package store
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
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 v := st.Get(start); v != nil {
|
||||
return cmn.KVPair{cp(start), cp(v)}, true
|
||||
} else {
|
||||
return kv, false
|
||||
}
|
||||
return kv, false
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ const (
|
|||
defaultIAVLNumHistory = 1<<53 - 1 // DEPRECATED
|
||||
)
|
||||
|
||||
// load the iavl store
|
||||
func LoadIAVLStore(db dbm.DB, id CommitID) (CommitStore, error) {
|
||||
tree := iavl.NewVersionedTree(db, defaultIAVLCacheSize)
|
||||
_, err := tree.LoadVersion(id.Version)
|
||||
|
|
|
@ -80,7 +80,7 @@ func TestIAVLIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, treeData[expectedKey])
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
|
||||
|
@ -91,7 +91,7 @@ func TestIAVLIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, treeData[expectedKey])
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
|
||||
|
@ -102,7 +102,7 @@ func TestIAVLIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, treeData[expectedKey])
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
|
||||
|
@ -113,7 +113,7 @@ func TestIAVLIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, treeData[expectedKey])
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
|
||||
|
@ -124,7 +124,7 @@ func TestIAVLIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, treeData[expectedKey])
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
|
||||
|
@ -135,7 +135,7 @@ func TestIAVLIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, treeData[expectedKey])
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ func TestIAVLSubspaceIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, expectedKey)
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
|
||||
|
@ -179,7 +179,7 @@ func TestIAVLSubspaceIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, []byte("test4"))
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
|
||||
|
@ -194,7 +194,7 @@ func TestIAVLSubspaceIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, []byte("test4"))
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ func TestIAVLReverseSubspaceIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, expectedKey)
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
|
||||
|
@ -238,7 +238,7 @@ func TestIAVLReverseSubspaceIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, []byte("test4"))
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
|
||||
|
@ -253,7 +253,7 @@ func TestIAVLReverseSubspaceIterator(t *testing.T) {
|
|||
key, value := iter.Key(), iter.Value()
|
||||
assert.EqualValues(t, key, expectedKey)
|
||||
assert.EqualValues(t, value, []byte("test4"))
|
||||
i += 1
|
||||
i++
|
||||
}
|
||||
assert.Equal(t, len(expected), i)
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ type rootMultiStore struct {
|
|||
var _ CommitMultiStore = (*rootMultiStore)(nil)
|
||||
var _ Queryable = (*rootMultiStore)(nil)
|
||||
|
||||
// nolint
|
||||
func NewCommitMultiStore(db dbm.DB) *rootMultiStore {
|
||||
return &rootMultiStore{
|
||||
db: db,
|
||||
|
@ -267,7 +268,7 @@ func (rs *rootMultiStore) loadCommitStoreFromParams(id CommitID, params storePar
|
|||
}
|
||||
|
||||
func (rs *rootMultiStore) nameToKey(name string) StoreKey {
|
||||
for key, _ := range rs.storesParams {
|
||||
for key := range rs.storesParams {
|
||||
if key.Name() == name {
|
||||
return key
|
||||
}
|
||||
|
|
|
@ -128,34 +128,34 @@ func TestMultiStoreQuery(t *testing.T) {
|
|||
// Test bad path.
|
||||
query := abci.RequestQuery{Path: "/key", Data: k, Height: ver}
|
||||
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"
|
||||
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.
|
||||
query.Path = "/garbage/key"
|
||||
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.
|
||||
query.Path = "/store1/key"
|
||||
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)
|
||||
|
||||
// Test valid but empty query.
|
||||
query.Path = "/store2/key"
|
||||
query.Prove = true
|
||||
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)
|
||||
|
||||
// Test store2 data.
|
||||
query.Data = k2
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
)
|
||||
|
||||
// Import cosmos-sdk/types/store.go for convenience.
|
||||
// nolint
|
||||
type Store = types.Store
|
||||
type Committer = types.Committer
|
||||
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))
|
||||
}
|
193
types/errors.go
193
types/errors.go
|
@ -2,25 +2,44 @@ package types
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// ABCI Response Code
|
||||
type CodeType uint32
|
||||
// ABCICodeType - combined codetype / codespace
|
||||
type ABCICodeType uint32
|
||||
|
||||
// is everything okay?
|
||||
func (code CodeType) IsOK() bool {
|
||||
if code == CodeOK {
|
||||
// CodeType - code identifier within codespace
|
||||
type CodeType uint16
|
||||
|
||||
// CodespaceType - codespace identifier
|
||||
type CodespaceType uint16
|
||||
|
||||
// IsOK - is everything okay?
|
||||
func (code ABCICodeType) IsOK() bool {
|
||||
if code == ABCICodeOK {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ABCI Response Codes
|
||||
// Base SDK reserves 0 - 99.
|
||||
// get the abci code from the local code and codespace
|
||||
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 (
|
||||
// ABCI error codes
|
||||
ABCICodeOK ABCICodeType = 0
|
||||
|
||||
// Base error codes
|
||||
CodeOK CodeType = 0
|
||||
CodeInternal CodeType = 1
|
||||
CodeTxDecode CodeType = 2
|
||||
|
@ -34,7 +53,14 @@ const (
|
|||
CodeInsufficientCoins CodeType = 10
|
||||
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.
|
||||
|
@ -44,8 +70,6 @@ func CodeToDefaultMsg(code CodeType) string {
|
|||
return "Internal error"
|
||||
case CodeTxDecode:
|
||||
return "Tx parse error"
|
||||
case CodeGenesisParse:
|
||||
return "Genesis parse error"
|
||||
case CodeInvalidSequence:
|
||||
return "Invalid sequence"
|
||||
case CodeUnauthorized:
|
||||
|
@ -75,40 +99,37 @@ func CodeToDefaultMsg(code CodeType) string {
|
|||
|
||||
// nolint
|
||||
func ErrInternal(msg string) Error {
|
||||
return newError(CodeInternal, msg)
|
||||
return newErrorWithRootCodespace(CodeInternal, msg)
|
||||
}
|
||||
func ErrTxDecode(msg string) Error {
|
||||
return newError(CodeTxDecode, msg)
|
||||
}
|
||||
func ErrGenesisParse(msg string) Error {
|
||||
return newError(CodeGenesisParse, msg)
|
||||
return newErrorWithRootCodespace(CodeTxDecode, msg)
|
||||
}
|
||||
func ErrInvalidSequence(msg string) Error {
|
||||
return newError(CodeInvalidSequence, msg)
|
||||
return newErrorWithRootCodespace(CodeInvalidSequence, msg)
|
||||
}
|
||||
func ErrUnauthorized(msg string) Error {
|
||||
return newError(CodeUnauthorized, msg)
|
||||
return newErrorWithRootCodespace(CodeUnauthorized, msg)
|
||||
}
|
||||
func ErrInsufficientFunds(msg string) Error {
|
||||
return newError(CodeInsufficientFunds, msg)
|
||||
return newErrorWithRootCodespace(CodeInsufficientFunds, msg)
|
||||
}
|
||||
func ErrUnknownRequest(msg string) Error {
|
||||
return newError(CodeUnknownRequest, msg)
|
||||
return newErrorWithRootCodespace(CodeUnknownRequest, msg)
|
||||
}
|
||||
func ErrInvalidAddress(msg string) Error {
|
||||
return newError(CodeInvalidAddress, msg)
|
||||
return newErrorWithRootCodespace(CodeInvalidAddress, msg)
|
||||
}
|
||||
func ErrUnknownAddress(msg string) Error {
|
||||
return newError(CodeUnknownAddress, msg)
|
||||
return newErrorWithRootCodespace(CodeUnknownAddress, msg)
|
||||
}
|
||||
func ErrInvalidPubKey(msg string) Error {
|
||||
return newError(CodeInvalidPubKey, msg)
|
||||
return newErrorWithRootCodespace(CodeInvalidPubKey, msg)
|
||||
}
|
||||
func ErrInsufficientCoins(msg string) Error {
|
||||
return newError(CodeInsufficientCoins, msg)
|
||||
return newErrorWithRootCodespace(CodeInsufficientCoins, msg)
|
||||
}
|
||||
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
|
||||
type Error interface {
|
||||
Error() string
|
||||
ABCICode() CodeType
|
||||
Code() CodeType
|
||||
Codespace() CodespaceType
|
||||
ABCILog() string
|
||||
ABCICode() ABCICodeType
|
||||
WithDefaultCodespace(codespace CodespaceType) Error
|
||||
Trace(msg string) Error
|
||||
TraceCause(cause error, msg string) Error
|
||||
Cause() error
|
||||
T() interface{}
|
||||
Result() Result
|
||||
QueryResult() abci.ResponseQuery
|
||||
}
|
||||
|
||||
func NewError(code CodeType, msg string) Error {
|
||||
return newError(code, msg)
|
||||
// NewError - create an error
|
||||
func NewError(codespace CodespaceType, code CodeType, msg string) Error {
|
||||
return newError(codespace, code, msg)
|
||||
}
|
||||
|
||||
type traceItem struct {
|
||||
msg string
|
||||
filename string
|
||||
lineno int
|
||||
func newErrorWithRootCodespace(code CodeType, msg string) *sdkError {
|
||||
return newError(CodespaceRoot, code, msg)
|
||||
}
|
||||
|
||||
func (ti traceItem) String() string {
|
||||
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.
|
||||
func newError(codespace CodespaceType, code CodeType, msg string) *sdkError {
|
||||
if msg == "" {
|
||||
msg = CodeToDefaultMsg(code)
|
||||
}
|
||||
return &sdkError{
|
||||
code: code,
|
||||
msg: msg,
|
||||
cause: nil,
|
||||
traces: nil,
|
||||
codespace: codespace,
|
||||
code: code,
|
||||
err: cmn.NewErrorWithT(code, msg),
|
||||
}
|
||||
}
|
||||
|
||||
type sdkError struct {
|
||||
codespace CodespaceType
|
||||
code CodeType
|
||||
err cmn.Error
|
||||
}
|
||||
|
||||
// Implements ABCIError.
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
// Implements ABCIError.
|
||||
func (err *sdkError) ABCILog() string {
|
||||
traceLog := ""
|
||||
for _, ti := range err.traces {
|
||||
traceLog += ti.String() + "\n"
|
||||
}
|
||||
return fmt.Sprintf("msg: %v\ntrace:\n%v",
|
||||
err.msg,
|
||||
traceLog,
|
||||
)
|
||||
return fmt.Sprintf(`=== ABCI Log ===
|
||||
Codespace: %v
|
||||
Code: %v
|
||||
ABCICode: %v
|
||||
Error: %#v
|
||||
=== /ABCI Log ===
|
||||
`, err.codespace, err.code, err.ABCICode(), err.err)
|
||||
}
|
||||
|
||||
// Add tracing information with msg.
|
||||
func (err *sdkError) Trace(msg string) Error {
|
||||
return err.doTrace(msg, 2)
|
||||
}
|
||||
|
||||
// Add tracing information with cause and msg.
|
||||
func (err *sdkError) TraceCause(cause error, msg string) Error {
|
||||
err.cause = cause
|
||||
return err.doTrace(msg, 2)
|
||||
}
|
||||
|
||||
func (err *sdkError) doTrace(msg string, n int) Error {
|
||||
_, fn, line, ok := runtime.Caller(n)
|
||||
if !ok {
|
||||
if fn == "" {
|
||||
fn = "<unknown>"
|
||||
}
|
||||
if line <= 0 {
|
||||
line = -1
|
||||
}
|
||||
return &sdkError{
|
||||
codespace: err.codespace,
|
||||
code: err.code,
|
||||
err: err.err.Trace(msg),
|
||||
}
|
||||
// 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
|
||||
// Implements Error.
|
||||
func (err *sdkError) WithDefaultCodespace(cs CodespaceType) Error {
|
||||
codespace := err.codespace
|
||||
if codespace == CodespaceUndefined {
|
||||
codespace = cs
|
||||
}
|
||||
return &sdkError{
|
||||
codespace: codespace,
|
||||
code: err.code,
|
||||
err: err.err,
|
||||
}
|
||||
}
|
||||
|
||||
func (err *sdkError) T() interface{} {
|
||||
return err.err.T()
|
||||
}
|
||||
|
||||
func (err *sdkError) Result() Result {
|
||||
|
|
|
@ -16,7 +16,6 @@ var codeTypes = []CodeType{
|
|||
CodeUnknownRequest,
|
||||
CodeUnknownAddress,
|
||||
CodeInvalidPubKey,
|
||||
CodeGenesisParse,
|
||||
}
|
||||
|
||||
type errFn func(msg string) Error
|
||||
|
@ -30,14 +29,12 @@ var errFns = []errFn{
|
|||
ErrUnknownRequest,
|
||||
ErrUnknownAddress,
|
||||
ErrInvalidPubKey,
|
||||
ErrGenesisParse,
|
||||
}
|
||||
|
||||
func TestCodeType(t *testing.T) {
|
||||
assert.True(t, CodeOK.IsOK())
|
||||
assert.True(t, ABCICodeOK.IsOK())
|
||||
|
||||
for _, c := range codeTypes {
|
||||
assert.False(t, c.IsOK())
|
||||
msg := CodeToDefaultMsg(c)
|
||||
assert.False(t, strings.HasPrefix(msg, "Unknown code"))
|
||||
}
|
||||
|
@ -47,7 +44,7 @@ func TestErrFn(t *testing.T) {
|
|||
for i, errFn := range errFns {
|
||||
err := errFn("")
|
||||
codeType := codeTypes[i]
|
||||
assert.Equal(t, err.ABCICode(), codeType)
|
||||
assert.Equal(t, err.Result().Code, codeType)
|
||||
assert.Equal(t, err.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) {
|
||||
case 1:
|
||||
if len(str[0]) == 0 {
|
||||
return f, NewError(CodeUnknownRequest, "not a decimal string")
|
||||
return f, ErrUnknownRequest("not a decimal string")
|
||||
}
|
||||
numStr = str[0]
|
||||
case 2:
|
||||
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]
|
||||
len := int64(len(str[1]))
|
||||
denom = new(big.Int).Exp(big.NewInt(10), big.NewInt(len), nil).Int64()
|
||||
default:
|
||||
return f, NewError(CodeUnknownRequest, "not a decimal string")
|
||||
return f, ErrUnknownRequest("not a decimal string")
|
||||
}
|
||||
|
||||
num, errConv := strconv.Atoi(numStr)
|
||||
if errConv != nil {
|
||||
return f, NewError(CodeUnknownRequest, errConv.Error())
|
||||
return f, ErrUnknownRequest(errConv.Error())
|
||||
}
|
||||
|
||||
if neg {
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
type Result struct {
|
||||
|
||||
// Code is the response code, is stored back on the chain.
|
||||
Code CodeType
|
||||
Code ABCICodeType
|
||||
|
||||
// Data is any data returned from the app.
|
||||
Data []byte
|
||||
|
@ -20,7 +20,7 @@ type Result struct {
|
|||
// GasWanted is the maximum units of work we allow this tx to perform.
|
||||
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
|
||||
|
||||
// Tx fee amount and denom.
|
||||
|
|
|
@ -25,15 +25,13 @@ func getVersion() string {
|
|||
}
|
||||
|
||||
// CMD
|
||||
|
||||
func printVersion(cmd *cobra.Command, args []string) {
|
||||
v := getVersion()
|
||||
fmt.Println(v)
|
||||
}
|
||||
|
||||
// REST
|
||||
|
||||
func VersionRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// version REST handler endpoint
|
||||
func RequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
v := getVersion()
|
||||
w.Write([]byte(v))
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// amino codec to marshal/unmarshal
|
||||
type Codec = amino.Codec
|
||||
|
||||
func NewCodec() *Codec {
|
||||
|
@ -15,10 +16,12 @@ func NewCodec() *Codec {
|
|||
return cdc
|
||||
}
|
||||
|
||||
// Register the go-crypto to the codec
|
||||
func RegisterCrypto(cdc *Codec) {
|
||||
crypto.RegisterAmino(cdc)
|
||||
}
|
||||
|
||||
// attempt to make some pretty json
|
||||
func MarshalJSONIndent(cdc *Codec, obj interface{}) ([]byte, error) {
|
||||
bz, err := cdc.MarshalJSON(obj)
|
||||
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) {
|
||||
_, result, abort := anteHandler(ctx, tx)
|
||||
assert.False(t, abort)
|
||||
assert.Equal(t, sdk.CodeOK, result.Code)
|
||||
assert.Equal(t, sdk.ABCICodeOK, result.Code)
|
||||
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) {
|
||||
_, result, abort := anteHandler(ctx, tx)
|
||||
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 {
|
||||
|
|
|
@ -17,6 +17,7 @@ func GetAccountCmdDefault(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
return GetAccountCmd(storeName, cdc, GetAccountDecoder(cdc))
|
||||
}
|
||||
|
||||
// Get account decoder for auth.DefaultAccount
|
||||
func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder {
|
||||
return func(accBytes []byte) (acct sdk.Account, err error) {
|
||||
// acct := new(auth.BaseAccount)
|
||||
|
|
|
@ -33,10 +33,12 @@ const (
|
|||
contextKeySigners contextKey = iota
|
||||
)
|
||||
|
||||
// add the signers to the context
|
||||
func WithSigners(ctx types.Context, accounts []types.Account) types.Context {
|
||||
return ctx.WithValue(contextKeySigners, accounts)
|
||||
}
|
||||
|
||||
// get the signers from the context
|
||||
func GetSigners(ctx types.Context) []types.Account {
|
||||
v := ctx.Value(contextKeySigners)
|
||||
if v == nil {
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
)
|
||||
|
||||
var _ sdk.AccountMapper = (*accountMapper)(nil)
|
||||
var _ sdk.AccountMapper = (*sealedAccountMapper)(nil)
|
||||
|
||||
// Implements sdk.AccountMapper.
|
||||
// This AccountMapper encodes/decodes accounts using the
|
||||
|
@ -28,6 +27,7 @@ type accountMapper struct {
|
|||
|
||||
// NewAccountMapper returns a new sdk.AccountMapper that
|
||||
// uses go-amino to (binary) encode and decode concrete sdk.Accounts.
|
||||
// nolint
|
||||
func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto sdk.Account) accountMapper {
|
||||
return accountMapper{
|
||||
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.
|
||||
func (am accountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) sdk.Account {
|
||||
acc := am.clonePrototype()
|
||||
|
@ -77,19 +62,6 @@ func (am accountMapper) SetAccount(ctx sdk.Context, acc sdk.Account) {
|
|||
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.
|
||||
|
||||
|
@ -107,14 +79,14 @@ func (am accountMapper) clonePrototype() sdk.Account {
|
|||
panic(fmt.Sprintf("accountMapper requires a proto sdk.Account, but %v doesn't implement sdk.Account", protoRt))
|
||||
}
|
||||
return clone
|
||||
} else {
|
||||
protoRv := reflect.New(protoRt).Elem()
|
||||
clone, ok := protoRv.Interface().(sdk.Account)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("accountMapper requires a proto sdk.Account, but %v doesn't implement sdk.Account", protoRt))
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
protoRv := reflect.New(protoRt).Elem()
|
||||
clone, ok := protoRv.Interface().(sdk.Account)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("accountMapper requires a proto sdk.Account, but %v doesn't implement sdk.Account", protoRt))
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
func (am accountMapper) encodeAccount(acc sdk.Account) []byte {
|
||||
|
|
|
@ -57,17 +57,3 @@ func TestAccountMapperGetSet(t *testing.T) {
|
|||
assert.NotNil(t, acc)
|
||||
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"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||
)
|
||||
|
||||
type commander struct {
|
||||
storeName string
|
||||
cdc *wire.Codec
|
||||
decoder sdk.AccountDecoder
|
||||
// register REST routes
|
||||
func RegisterRoutes(r *mux.Router, cdc *wire.Codec, storeName string) {
|
||||
r.HandleFunc(
|
||||
"/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) {
|
||||
c := commander{storeName, cdc, decoder}
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
@ -33,7 +36,7 @@ func QueryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder sdk.A
|
|||
}
|
||||
key := sdk.Address(bz)
|
||||
|
||||
res, err := ctx.Query(key, c.storeName)
|
||||
res, err := ctx.Query(key, storeName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
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
|
||||
account, err := c.decoder(res)
|
||||
account, err := decoder(res)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
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