Merge branch 'develop' into sunny/IAVLsubspace

This commit is contained in:
Sunny Aggarwal 2018-04-05 22:02:21 +02:00 committed by GitHub
commit 348e28a4b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
111 changed files with 4633 additions and 1880 deletions

18
.gitignore vendored
View File

@ -1,23 +1,23 @@
# OS
.DS_Store
*.swp
*.swo
.vscode
# Build
vendor
merkleeyes.db
build
docs/guide/*.sh
tools/bin/*
examples/build/*
examples/basecoin/glide.lock
docs/_build
# Data - ideally these don't exist
examples/basecoin/app/data
baseapp/data/*
docs/_build
.DS_Store
# Testing
coverage.txt
profile.out
.vscode
coverage.txt
profile.out
client/lcd/keys.db/
### Vagrant ###
.vagrant/

View File

@ -1,5 +1,44 @@
# Changelog
## 0.13.1 (April 3, 2018)
BUG FIXES
* [x/ibc] Fix CLI and relay for IBC txs
* [x/stake] Various fixes/improvements
## 0.13.0 (April 2, 2018)
BREAKING CHANGES
* [basecoin] Remove cool/sketchy modules -> moved to new `democoin`
* [basecoin] NewBasecoinApp takes a `map[string]dbm.DB` as temporary measure
to allow mounting multiple stores with their own DB until they can share one
* [x/staking] Renamed to `simplestake`
* [builder] Functions don't take `passphrase` as argument
* [server] GenAppState returns generated seed and address
* [basecoind] `init` command outputs JSON of everything necessary for testnet
* [basecoind] `basecoin.db -> data/basecoin.db`
* [basecli] `data/keys.db -> keys/keys.db`
FEATURES
* [types] `Coin` supports direct arithmetic operations
* [basecoind] Add `show_validator` and `show_node_id` commands
* [x/stake] Initial merge of full staking module!
* [democoin] New example application to demo custom modules
IMPROVEMENTS
* [makefile] `make install`
* [testing] Use `/tmp` for directories so they don't get left in the repo
BUG FIXES
* [basecoin] Allow app to be restarted
* [makefile] Fix build on Windows
* [basecli] Get confirmation before overriding key with same name
## 0.12.0 (March 27 2018)
BREAKING CHANGES
@ -10,9 +49,9 @@ BREAKING CHANGES
* [types] Replace tx.GetFeePayer with FeePayer(tx) - returns the first signer
* [types] NewStdTx takes the Fee
* [types] ParseAccount -> AccountDecoder; ErrTxParse -> ErrTxDecoder
* [auth] AnteHandler deducts fees
* [bank] Move some errors to `types`
* [bank] Remove sequence and signature from Input
* [x/auth] AnteHandler deducts fees
* [x/bank] Move some errors to `types`
* [x/bank] Remove sequence and signature from Input
FEATURES
@ -26,7 +65,7 @@ FEATURES
* [types] StdFee, and StdTx takes the StdFee
* [specs] Progression of MVPs for IBC
* [x/ibc] Initial shell of IBC functionality (no proofs)
* [x/staking] Simple staking module with bonding/unbonding
* [x/simplestake] Simple staking module with bonding/unbonding
IMPROVEMENTS
@ -36,8 +75,8 @@ IMPROVEMENTS
* [specs] Staking
BUG FIXES
* [auth] Fix setting pubkey on new account
* [auth] Require signatures to include the sequences
* [x/auth] Fix setting pubkey on new account
* [x/auth] Require signatures to include the sequences
* [baseapp] Dont panic on nil handler
* [basecoin] Check for empty bytes in account and tx

32
Gopkg.lock generated
View File

@ -117,7 +117,7 @@
"json/scanner",
"json/token"
]
revision = "f40e974e75af4e271d97ce0fc917af5898ae7bda"
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
[[projects]]
branch = "master"
@ -191,8 +191,8 @@
".",
"mem"
]
revision = "bb8f1927f2a9d3ab41c9340aa034f6b803f4359c"
version = "v1.0.2"
revision = "63644898a8da0bc22138abf860edaf5277b6102e"
version = "v1.1.0"
[[projects]]
name = "github.com/spf13/cast"
@ -203,8 +203,8 @@
[[projects]]
name = "github.com/spf13/cobra"
packages = ["."]
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
version = "v0.0.1"
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
version = "v0.0.2"
[[projects]]
branch = "master"
@ -250,7 +250,7 @@
"leveldb/table",
"leveldb/util"
]
revision = "169b1b37be738edb2813dab48c97a549bcf99bb5"
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
[[projects]]
name = "github.com/tendermint/abci"
@ -341,8 +341,8 @@
"version",
"wire"
]
revision = "6f9956990c444d53f62f2a3905ed410cfe9afe77"
version = "v0.17.1"
revision = "a1dd329d72e78d4770e602359bad5b7b1e8b72a3"
version = "v0.18.0-rc1"
[[projects]]
name = "github.com/tendermint/tmlibs"
@ -359,8 +359,8 @@
"pubsub",
"pubsub/query"
]
revision = "24da7009c3d8c019b40ba4287495749e3160caca"
version = "v0.7.1"
revision = "2e24b64fc121dcdf1cabceab8dc2f7257675483c"
version = "0.8.1"
[[projects]]
branch = "master"
@ -376,7 +376,7 @@
"ripemd160",
"salsa20/salsa"
]
revision = "88942b9c40a4c9d203b82b3731787b672d6e809b"
revision = "b2aa35443fbc700ab74c586ae79b81c171851023"
[[projects]]
branch = "master"
@ -390,13 +390,13 @@
"lex/httplex",
"trace"
]
revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
revision = "b3c676e531a6dc479fa1b35ac961c13f5e2b4d2e"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "13d03a9a82fba647c21a0ef8fba44a795d0f0835"
revision = "1d206c9fa8975fb4cf00df1dc8bf3283dc24ba0e"
[[projects]]
name = "golang.org/x/text"
@ -423,7 +423,7 @@
branch = "master"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "ab0870e398d5dd054b868c0db1481ab029b9a9f2"
revision = "35de2414665fc36f56b72d982c5af480d86de5ab"
[[projects]]
name = "google.golang.org/grpc"
@ -452,8 +452,8 @@
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "86f5ed62f8a0ee96bd888d2efdfd6d4fb100a4eb"
version = "v2.2.0"
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[solve-meta]
analyzer-name = "dep"

View File

@ -70,11 +70,11 @@
name = "github.com/tendermint/iavl"
[[constraint]]
version = "~0.17.1"
version = "~0.18.0-rc1"
name = "github.com/tendermint/tendermint"
[[constraint]]
version = "~0.7.1"
[[override]]
version = "~0.8.1"
name = "github.com/tendermint/tmlibs"
[prune]

View File

@ -2,7 +2,7 @@ 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 test
all: check_tools get_vendor_deps build build_examples test
########################################
### CI
@ -13,20 +13,38 @@ ci: get_tools get_vendor_deps build test_cover
### Build
# This can be unified later, here for easy demos
gaia:
go build $(BUILD_FLAGS) -o build/gaiad ./examples/gaia/gaiad
go build $(BUILD_FLAGS) -o build/gaiacli ./examples/gaia/gaiacli
build:
@rm -rf $(shell pwd)/examples/basecoin/vendor/
ifeq ($(OS),Windows_NT)
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaiad
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaiacli
else
go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaiad
go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaiacli
endif
build_examples:
ifeq ($(OS),Windows_NT)
go build $(BUILD_FLAGS) -o build/basecoind.exe ./examples/basecoin/cmd/basecoind
go build $(BUILD_FLAGS) -o build/basecli.exe ./examples/basecoin/cmd/basecli
go build $(BUILD_FLAGS) -o build/democoind.exe ./examples/democoin/cmd/democoind
go build $(BUILD_FLAGS) -o build/democli.exe ./examples/democoin/cmd/democli
else
go build $(BUILD_FLAGS) -o build/basecoind ./examples/basecoin/cmd/basecoind
go build $(BUILD_FLAGS) -o build/basecli ./examples/basecoin/cmd/basecli
go build $(BUILD_FLAGS) -o build/democoind ./examples/democoin/cmd/democoind
go build $(BUILD_FLAGS) -o build/democli ./examples/democoin/cmd/democli
endif
install:
go install $(BUILD_FLAGS) ./cmd/gaiad
go install $(BUILD_FLAGS) ./cmd/gaiacli
install_examples:
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecoind
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecli
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democoind
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democli
dist:
@bash publish/dist.sh
@bash publish/publish.sh
@ -73,14 +91,10 @@ test: test_unit # test_cli
# go test -coverprofile=c.out && go tool cover -html=c.out
test_unit:
@rm -rf examples/basecoin/vendor/
@go test $(PACKAGES)
test_cover:
@rm -rf examples/basecoin/vendor/
@rm -rf client/lcd/keys.db ~/.tendermint_test
@bash tests/test_cover.sh
@rm -rf client/lcd/keys.db ~/.tendermint_test
benchmark:
@go test -bench=. $(PACKAGES)
@ -113,4 +127,4 @@ devdoc_update:
# To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: build dist check_tools get_tools get_vendor_deps draw_deps test test_unit test_tutorial benchmark devdoc_init devdoc devdoc_save devdoc_update
.PHONY: build build_examples install install_examples dist check_tools get_tools get_vendor_deps draw_deps test test_unit test_tutorial benchmark devdoc_init devdoc devdoc_save devdoc_update

View File

@ -55,6 +55,7 @@ type BaseApp struct {
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,
@ -71,12 +72,18 @@ func (app *BaseApp) Name() string {
}
// Mount a store to the provided key in the BaseApp multistore
// Broken until #532 is implemented.
func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) {
for _, key := range keys {
app.MountStore(key, sdk.StoreTypeIAVL)
}
}
// Mount a store to the provided key in the BaseApp multistore
func (app *BaseApp) MountStoreWithDB(key sdk.StoreKey, typ sdk.StoreType, db dbm.DB) {
app.cms.MountStoreWithDB(key, typ, db)
}
// Mount a store to the provided key in the BaseApp multistore
func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) {
app.cms.MountStoreWithDB(key, typ, app.db)
@ -241,11 +248,6 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
// TODO Return something intelligent
panic(err)
}
err = app.Router().InitGenesis(app.deliverState.ctx, *genesisState)
if err != nil {
// TODO Return something intelligent
panic(err)
}
// NOTE: we don't commit, but BeginBlock for block 1
// starts from this deliverState
@ -259,7 +261,7 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
queryable, ok := app.cms.(sdk.Queryable)
if !ok {
msg := "application doesn't support queries"
return sdk.ErrUnknownRequest(msg).Result().ToQuery()
return sdk.ErrUnknownRequest(msg).QueryResult()
}
return queryable.Query(req)
}

View File

@ -35,12 +35,15 @@ func TestMountStores(t *testing.T) {
// make some cap keys
capKey1 := sdk.NewKVStoreKey("key1")
db1 := dbm.NewMemDB()
capKey2 := sdk.NewKVStoreKey("key2")
db2 := dbm.NewMemDB()
// no stores are mounted
assert.Panics(t, func() { app.LoadLatestVersion(capKey1) })
app.MountStoresIAVL(capKey1, capKey2)
app.MountStoreWithDB(capKey1, sdk.StoreTypeIAVL, db1)
app.MountStoreWithDB(capKey2, sdk.StoreTypeIAVL, db2)
// stores are mounted
err := app.LoadLatestVersion(capKey1)
@ -126,7 +129,6 @@ func TestTxDecoder(t *testing.T) {
// Test that Info returns the latest committed state.
func TestInfo(t *testing.T) {
app := newBaseApp(t.Name())
// ----- test an empty response -------
@ -145,17 +147,19 @@ func TestInfo(t *testing.T) {
}
func TestInitChainer(t *testing.T) {
logger := defaultLogger()
db := dbm.NewMemDB()
name := t.Name()
db := dbm.NewMemDB()
logger := defaultLogger()
app := NewBaseApp(name, logger, db)
// make cap keys and mount the stores
// NOTE/TODO: mounting multiple stores is broken
// see https://github.com/cosmos/cosmos-sdk/issues/532
capKey := sdk.NewKVStoreKey("main")
// capKey2 := sdk.NewKVStoreKey("key2")
app.MountStoresIAVL(capKey) // , capKey2)
db1 := dbm.NewMemDB()
capKey2 := sdk.NewKVStoreKey("key2")
db2 := dbm.NewMemDB()
app.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db1)
app.MountStoreWithDB(capKey2, sdk.StoreTypeIAVL, db2)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
@ -187,9 +191,8 @@ func TestInitChainer(t *testing.T) {
// reload app
app = NewBaseApp(name, logger, db)
capKey = sdk.NewKVStoreKey("main")
// capKey2 = sdk.NewKVStoreKey("key2") // TODO
app.MountStoresIAVL(capKey) //, capKey2)
app.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db1)
app.MountStoreWithDB(capKey2, sdk.StoreTypeIAVL, db2)
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
app.SetInitChainer(initChainer)
@ -246,7 +249,7 @@ func TestDeliverTx(t *testing.T) {
counter += 1
return sdk.Result{}
}, nil)
})
tx := testUpdatePowerTx{} // doesn't matter
header := abci.Header{AppHash: []byte("apphash")}
@ -281,7 +284,7 @@ func TestQuery(t *testing.T) {
store := ctx.KVStore(capKey)
store.Set(key, value)
return sdk.Result{}
}, nil)
})
query := abci.RequestQuery{
Path: "/main/key",
@ -346,7 +349,7 @@ func TestValidatorChange(t *testing.T) {
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
// TODO
return sdk.Result{}
}, nil)
})
// Load latest state, which should be empty.
err := app.LoadLatestVersion(capKey)

View File

@ -1,8 +1,6 @@
package baseapp
import (
"encoding/json"
"fmt"
"regexp"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -10,16 +8,14 @@ import (
// Router provides handlers for each transaction type.
type Router interface {
AddRoute(r string, h sdk.Handler, i sdk.InitGenesis) (rtr Router)
AddRoute(r string, h sdk.Handler) (rtr Router)
Route(path string) (h sdk.Handler)
InitGenesis(ctx sdk.Context, data map[string]json.RawMessage) error
}
// map a transaction type to a handler and an initgenesis function
type route struct {
r string
h sdk.Handler
i sdk.InitGenesis
}
type router struct {
@ -38,11 +34,11 @@ func NewRouter() *router {
var isAlpha = regexp.MustCompile(`^[a-zA-Z]+$`).MatchString
// AddRoute - TODO add description
func (rtr *router) AddRoute(r string, h sdk.Handler, i sdk.InitGenesis) Router {
func (rtr *router) AddRoute(r string, h sdk.Handler) Router {
if !isAlpha(r) {
panic("route expressions can only contain alphanumeric characters")
}
rtr.routes = append(rtr.routes, route{r, h, i})
rtr.routes = append(rtr.routes, route{r, h})
return rtr
}
@ -57,20 +53,3 @@ func (rtr *router) Route(path string) (h sdk.Handler) {
}
return nil
}
// InitGenesis - call `InitGenesis`, where specified, for all routes
// Return the first error if any, otherwise nil
func (rtr *router) InitGenesis(ctx sdk.Context, data map[string]json.RawMessage) error {
for _, route := range rtr.routes {
if route.i != nil {
encoded, found := data[route.r]
if !found {
return sdk.ErrGenesisParse(fmt.Sprintf("Expected module genesis information for module %s but it was not present", route.r))
}
if err := route.i(ctx, encoded); err != nil {
return err
}
}
}
return nil
}

27
client/context/viper.go Normal file
View File

@ -0,0 +1,27 @@
package context
import (
"github.com/spf13/viper"
rpcclient "github.com/tendermint/tendermint/rpc/client"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/core"
)
func NewCoreContextFromViper() core.CoreContext {
nodeURI := viper.GetString(client.FlagNode)
var rpc rpcclient.Client
if nodeURI != "" {
rpc = rpcclient.NewHTTP(nodeURI, "/websocket")
}
return core.CoreContext{
ChainID: viper.GetString(client.FlagChainID),
Height: viper.GetInt64(client.FlagHeight),
TrustNode: viper.GetBool(client.FlagTrustNode),
FromAddressName: viper.GetString(client.FlagName),
NodeURI: nodeURI,
Sequence: viper.GetInt64(client.FlagSequence),
Client: rpc,
}
}

50
client/core/context.go Normal file
View File

@ -0,0 +1,50 @@
package core
import (
rpcclient "github.com/tendermint/tendermint/rpc/client"
)
type CoreContext struct {
ChainID string
Height int64
TrustNode bool
NodeURI string
FromAddressName string
Sequence int64
Client rpcclient.Client
}
func (c CoreContext) WithChainID(chainID string) CoreContext {
c.ChainID = chainID
return c
}
func (c CoreContext) WithHeight(height int64) CoreContext {
c.Height = height
return c
}
func (c CoreContext) WithTrustNode(trustNode bool) CoreContext {
c.TrustNode = trustNode
return c
}
func (c CoreContext) WithNodeURI(nodeURI string) CoreContext {
c.NodeURI = nodeURI
return c
}
func (c CoreContext) WithFromAddressName(fromAddressName string) CoreContext {
c.FromAddressName = fromAddressName
return c
}
func (c CoreContext) WithSequence(sequence int64) CoreContext {
c.Sequence = sequence
return c
}
func (c CoreContext) WithClient(client rpcclient.Client) CoreContext {
c.Client = client
return c
}

View File

@ -1,10 +1,9 @@
package builder
package core
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/wire"
rpcclient "github.com/tendermint/tendermint/rpc/client"
@ -17,9 +16,9 @@ import (
)
// Broadcast the transaction bytes to Tendermint
func BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
func (ctx CoreContext) BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
node, err := client.GetNode()
node, err := ctx.GetNode()
if err != nil {
return nil, err
}
@ -43,17 +42,17 @@ func BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
}
// Query from Tendermint with the provided key and storename
func Query(key cmn.HexBytes, storeName string) (res []byte, err error) {
func (ctx CoreContext) Query(key cmn.HexBytes, storeName string) (res []byte, err error) {
path := fmt.Sprintf("/%s/key", storeName)
node, err := client.GetNode()
node, err := ctx.GetNode()
if err != nil {
return res, err
}
opts := rpcclient.ABCIQueryOptions{
Height: viper.GetInt64(client.FlagHeight),
Trusted: viper.GetBool(client.FlagTrustNode),
Height: ctx.Height,
Trusted: ctx.TrustNode,
}
result, err := node.ABCIQueryWithOptions(path, key, opts)
if err != nil {
@ -67,16 +66,16 @@ func Query(key cmn.HexBytes, storeName string) (res []byte, err error) {
}
// Get the from address from the name flag
func GetFromAddress() (from sdk.Address, err error) {
func (ctx CoreContext) GetFromAddress() (from sdk.Address, err error) {
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
name := viper.GetString(client.FlagName)
name := ctx.FromAddressName
if name == "" {
return nil, errors.Errorf("must provide a name using --name")
return nil, errors.Errorf("must provide a from address name")
}
info, err := keybase.Get(name)
@ -88,11 +87,11 @@ func GetFromAddress() (from sdk.Address, err error) {
}
// sign and build the transaction from the msg
func SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *wire.Codec) ([]byte, error) {
func (ctx CoreContext) SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *wire.Codec) ([]byte, error) {
// build the Sign Messsage from the Standard Message
chainID := viper.GetString(client.FlagChainID)
sequence := int64(viper.GetInt(client.FlagSequence))
chainID := ctx.ChainID
sequence := ctx.Sequence
signMsg := sdk.StdSignMsg{
ChainID: chainID,
Sequences: []int64{sequence},
@ -114,7 +113,7 @@ func SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *wire.Codec) ([]byte
sigs := []sdk.StdSignature{{
PubKey: pubkey,
Signature: sig,
Sequence: viper.GetInt64(client.FlagSequence),
Sequence: sequence,
}}
// marshal bytes
@ -124,23 +123,31 @@ func SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *wire.Codec) ([]byte
}
// sign and build the transaction from the msg
func SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (*ctypes.ResultBroadcastTxCommit, error) {
passphrase, err := GetPassphraseFromStdin(name)
func (ctx CoreContext) SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (*ctypes.ResultBroadcastTxCommit, error) {
passphrase, err := ctx.GetPassphraseFromStdin(name)
if err != nil {
return nil, err
}
txBytes, err := SignAndBuild(name, passphrase, msg, cdc)
txBytes, err := ctx.SignAndBuild(name, passphrase, msg, cdc)
if err != nil {
return nil, err
}
return BroadcastTx(txBytes)
return ctx.BroadcastTx(txBytes)
}
// get passphrase from std input
func GetPassphraseFromStdin(name string) (pass string, err error) {
func (ctx CoreContext) GetPassphraseFromStdin(name string) (pass string, err error) {
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password to sign with '%s':", name)
return client.GetPassword(prompt, buf)
}
// GetNode prepares a simple rpc.Client
func (ctx CoreContext) GetNode() (rpcclient.Client, error) {
if ctx.Client == nil {
return nil, errors.New("Must define node URI")
}
return ctx.Client, nil
}

View File

@ -1,17 +0,0 @@
package client
import (
"github.com/pkg/errors"
"github.com/spf13/viper"
rpcclient "github.com/tendermint/tendermint/rpc/client"
)
// GetNode prepares a simple rpc.Client from the flags
func GetNode() (rpcclient.Client, error) {
uri := viper.GetString(FlagNode)
if uri == "" {
return nil, errors.New("Must define node using --node")
}
return rpcclient.NewHTTP(uri, "/websocket"), nil
}

View File

@ -2,6 +2,7 @@ package keys
import (
"fmt"
"path/filepath"
"github.com/spf13/viper"
@ -32,7 +33,7 @@ type KeyOutput struct {
func GetKeyBase() (keys.Keybase, error) {
if keybase == nil {
rootDir := viper.GetString(cli.HomeFlag)
db, err := dbm.NewGoLevelDB(KeyDBName, rootDir)
db, err := dbm.NewGoLevelDB(KeyDBName, filepath.Join(rootDir, "keys"))
if err != nil {
return nil, err
}

View File

@ -1 +0,0 @@
tmp-base*

View File

@ -25,6 +25,7 @@ import (
ctypes "github.com/tendermint/tendermint/rpc/core/types"
tmrpc "github.com/tendermint/tendermint/rpc/lib/server"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -157,7 +158,7 @@ func TestNodeStatus(t *testing.T) {
func TestBlock(t *testing.T) {
time.Sleep(time.Second * 2) // TODO: LOL -> wait for blocks
waitForHeight(2)
var resultBlock ctypes.ResultBlock
@ -221,8 +222,7 @@ func TestCoinSend(t *testing.T) {
// create TX
receiveAddr, resultTx := doSend(t, port, seed)
time.Sleep(time.Second * 2) // T
waitForHeight(resultTx.Height + 1)
// check if tx was commited
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
@ -252,6 +252,32 @@ func TestCoinSend(t *testing.T) {
assert.Equal(t, int64(1), mycoins.Amount)
}
func TestIBCTransfer(t *testing.T) {
// create TX
resultTx := doIBCTransfer(t, port, seed)
waitForHeight(resultTx.Height + 1)
// check if tx was commited
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
// query sender
res, body := request(t, port, "GET", "/accounts/"+sendAddr, nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var m auth.BaseAccount
err := json.Unmarshal([]byte(body), &m)
require.Nil(t, err)
coins := m.Coins
mycoins := coins[0]
assert.Equal(t, coinDenom, mycoins.Denom)
assert.Equal(t, coinAmount-2, mycoins.Amount)
// TODO: query ibc egress packet state
}
func TestTxs(t *testing.T) {
// TODO: re-enable once we can get txs by tag
@ -269,7 +295,7 @@ func TestTxs(t *testing.T) {
// create TX
_, resultTx := doSend(t, port, seed)
time.Sleep(time.Second * 2) // TO
waitForHeight(resultTx.Height + 1)
// check if tx is findable
res, body := request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
@ -294,6 +320,7 @@ 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())
kb, err := keys.GetKeyBase() // dbm.NewMemDB()) // :(
if err != nil {
return nil, nil, err
@ -315,7 +342,13 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
logger = log.NewFilter(logger, log.AllowError())
privValidatorFile := config.PrivValidatorFile()
privVal := tmtypes.LoadOrGenPrivValidatorFS(privValidatorFile)
app := bapp.NewBasecoinApp(logger, dbm.NewMemDB())
dbs := map[string]dbm.DB{
"main": dbm.NewMemDB(),
"acc": dbm.NewMemDB(),
"ibc": dbm.NewMemDB(),
"staking": dbm.NewMemDB(),
}
app := bapp.NewBasecoinApp(logger, dbs)
genesisFile := config.GenesisFile()
genDoc, err := tmtypes.GenesisDocFromFile(genesisFile)
@ -332,9 +365,6 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
Coins: coins,
},
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.Marshal(appState)
if err != nil {
@ -361,7 +391,7 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
return nil, nil, err
}
time.Sleep(time.Second * 2)
waitForStart()
return node, lcd, nil
}
@ -411,6 +441,7 @@ func request(t *testing.T, port, method, path string, payload []byte) (*http.Res
require.Nil(t, err)
output, err := ioutil.ReadAll(res.Body)
res.Body.Close()
require.Nil(t, err)
return res, string(output)
@ -430,8 +461,6 @@ func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctype
acc := auth.BaseAccount{}
err = json.Unmarshal([]byte(body), &acc)
require.Nil(t, err)
fmt.Println("BODY", body)
fmt.Println("ACC", acc)
sequence := acc.Sequence
// send
@ -444,3 +473,99 @@ func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctype
return receiveAddr, resultTx
}
func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) {
// create receive address
kb := client.MockKeyBase()
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
require.Nil(t, err)
receiveAddr := receiveInfo.PubKey.Address().String()
// get the account to get the sequence
res, body := request(t, port, "GET", "/accounts/"+sendAddr, nil)
// require.Equal(t, http.StatusOK, res.StatusCode, body)
acc := auth.BaseAccount{}
err = json.Unmarshal([]byte(body), &acc)
require.Nil(t, err)
sequence := acc.Sequence
// send
jsonStr := []byte(fmt.Sprintf(`{ "name":"%s", "password":"%s", "sequence":%d, "amount":[{ "denom": "%s", "amount": 1 }] }`, name, password, sequence, coinDenom))
res, body = request(t, port, "POST", "/ibc/testchain/"+receiveAddr+"/send", jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err = json.Unmarshal([]byte(body), &resultTx)
require.Nil(t, err)
return resultTx
}
func waitForHeight(height int64) {
for {
var resultBlock ctypes.ResultBlock
url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest")
res, err := http.Get(url)
if err != nil {
panic(err)
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
res.Body.Close()
err = json.Unmarshal([]byte(body), &resultBlock)
if err != nil {
fmt.Println("RES", res)
fmt.Println("BODY", string(body))
panic(err)
}
if resultBlock.Block.Height >= height {
return
}
time.Sleep(time.Millisecond * 100)
}
}
// wait for 2 blocks
func waitForStart() {
waitHeight := int64(2)
for {
time.Sleep(time.Second)
var resultBlock ctypes.ResultBlock
url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest")
res, err := http.Get(url)
if err != nil {
panic(err)
}
// waiting for server to start ...
if res.StatusCode != http.StatusOK {
res.Body.Close()
continue
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
res.Body.Close()
err = json.Unmarshal([]byte(body), &resultBlock)
if err != nil {
fmt.Println("RES", res)
fmt.Println("BODY", string(body))
panic(err)
}
if resultBlock.Block.Height >= waitHeight {
return
}
}
}

View File

@ -20,6 +20,7 @@ import (
"github.com/cosmos/cosmos-sdk/wire"
auth "github.com/cosmos/cosmos-sdk/x/auth/rest"
bank "github.com/cosmos/cosmos-sdk/x/bank/rest"
ibc "github.com/cosmos/cosmos-sdk/x/ibc/rest"
)
const (
@ -78,5 +79,6 @@ func createHandler(cdc *wire.Codec) http.Handler {
tx.RegisterRoutes(r, cdc)
auth.RegisterRoutes(r, cdc, "main")
bank.RegisterRoutes(r, cdc, kb)
ibc.RegisterRoutes(r, cdc, kb)
return r
}

View File

@ -10,6 +10,7 @@ import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
)
const (
@ -31,7 +32,8 @@ func blockCommand() *cobra.Command {
func getBlock(height *int64) ([]byte, error) {
// get the node
node, err := client.GetNode()
ctx := context.NewCoreContextFromViper()
node, err := ctx.GetNode()
if err != nil {
return nil, err
}
@ -55,7 +57,7 @@ func getBlock(height *int64) ([]byte, error) {
}
func GetChainHeight() (int64, error) {
node, err := client.GetNode()
node, err := context.NewCoreContextFromViper().GetNode()
if err != nil {
return -1, err
}

View File

@ -10,6 +10,7 @@ import (
wire "github.com/tendermint/go-wire"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
)
@ -25,7 +26,7 @@ func statusCommand() *cobra.Command {
func getNodeStatus() (*ctypes.ResultStatus, error) {
// get the node
node, err := client.GetNode()
node, err := context.NewCoreContextFromViper().GetNode()
if err != nil {
return &ctypes.ResultStatus{}, err
}

View File

@ -10,6 +10,7 @@ import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
)
func validatorCommand() *cobra.Command {
@ -26,7 +27,7 @@ func validatorCommand() *cobra.Command {
func GetValidators(height *int64) ([]byte, error) {
// get the node
node, err := client.GetNode()
node, err := context.NewCoreContextFromViper().GetNode()
if err != nil {
return nil, err
}

View File

@ -4,7 +4,7 @@ import (
"encoding/json"
"net/http"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/client/context"
)
type BroadcastTxBody struct {
@ -22,7 +22,7 @@ func BroadcastTxRequestHandler(w http.ResponseWriter, r *http.Request) {
return
}
res, err := builder.BroadcastTx([]byte(m.TxBytes))
res, err := context.NewCoreContextFromViper().BroadcastTx([]byte(m.TxBytes))
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))

View File

@ -15,6 +15,7 @@ import (
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
@ -39,7 +40,7 @@ func (c commander) queryTx(hashHexStr string, trustNode bool) ([]byte, error) {
}
// get the node
node, err := client.GetNode()
node, err := context.NewCoreContextFromViper().GetNode()
if err != nil {
return nil, err
}

View File

@ -12,6 +12,7 @@ import (
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/wire"
)
@ -43,7 +44,7 @@ func (c commander) searchTx(tags []string) ([]byte, error) {
query := strings.Join(tags, " AND ")
// get the node
node, err := client.GetNode()
node, err := context.NewCoreContextFromViper().GetNode()
if err != nil {
return nil, err
}

View File

@ -17,9 +17,9 @@ const (
flagFee = "fee"
)
// gaiacliCmd is the entry point for this binary
// rootCmd is the entry point for this binary
var (
gaiacliCmd = &cobra.Command{
rootCmd = &cobra.Command{
Use: "gaiacli",
Short: "Gaia light-client",
}
@ -54,16 +54,16 @@ func main() {
cobra.EnableCommandSorting = false
// generic client commands
AddClientCommands(gaiacliCmd)
AddClientCommands(rootCmd)
// query commands (custom to binary)
gaiacliCmd.AddCommand(
rootCmd.AddCommand(
GetCommands(getAccountCmd)...)
// post tx commands (custom to binary)
gaiacliCmd.AddCommand(
rootCmd.AddCommand(
PostCommands(postSendCommand())...)
// add proxy, version and key info
gaiacliCmd.AddCommand(
rootCmd.AddCommand(
lineBreak,
serveCommand(),
KeyCommands(),
@ -72,6 +72,6 @@ func main() {
)
// prepare and add flags
executor := cli.PrepareBaseCmd(gaiacliCmd, "GA", os.ExpandEnv("$HOME/.gaiacli"))
executor := cli.PrepareBaseCmd(rootCmd, "GA", os.ExpandEnv("$HOME/.gaiacli"))
executor.Execute()
}

63
cmd/gaiad/main.go Normal file
View File

@ -0,0 +1,63 @@
package main
import (
"os"
"path/filepath"
"github.com/spf13/cobra"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
"github.com/cosmos/cosmos-sdk/server"
)
// rootCmd is the entry point for this binary
var (
context = server.NewDefaultContext()
rootCmd = &cobra.Command{
Use: "gaiad",
Short: "Gaia Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(context),
}
)
// TODO: distinguish from basecoin
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
dataDir := filepath.Join(rootDir, "data")
dbMain, err := dbm.NewGoLevelDB("gaia", dataDir)
if err != nil {
return nil, err
}
dbAcc, err := dbm.NewGoLevelDB("gaia-acc", dataDir)
if err != nil {
return nil, err
}
dbIBC, err := dbm.NewGoLevelDB("gaia-ibc", dataDir)
if err != nil {
return nil, err
}
dbStaking, err := dbm.NewGoLevelDB("gaia-staking", dataDir)
if err != nil {
return nil, err
}
dbs := map[string]dbm.DB{
"main": dbMain,
"acc": dbAcc,
"ibc": dbIBC,
"staking": dbStaking,
}
bapp := app.NewBasecoinApp(logger, dbs)
return bapp, nil
}
func main() {
server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context)
// prepare and add flags
executor := cli.PrepareBaseCmd(rootCmd, "GA", os.ExpandEnv("$HOME/.gaiad"))
executor.Execute()
}

View File

@ -1,204 +0,0 @@
Cosmos-SDK Basecoin (template)
License: Apache2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 All in Bits, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,22 +0,0 @@
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/examples/basecoin/version.GitCommit=`git rev-parse --short HEAD`"
all: get_tools get_vendor_deps build test
get_tools:
go get github.com/golang/dep/cmd/dep
build:
go build $(BUILD_FLAGS) -o build/basecoin ./cmd/...
get_vendor_deps:
@rm -rf vendor/
@dep ensure
test:
@go test $(PACKAGES)
benchmark:
@go test -bench=. $(PACKAGES)
.PHONY: all build test benchmark

View File

@ -1,70 +0,0 @@
# Basecoin
This is the "Basecoin" example application built on the Cosmos-Sdk. This
"Basecoin" is not affiliated with [Coinbase](http://www.getbasecoin.com/), nor
the [stable coin](http://www.getbasecoin.com/).
Assuming you've run `make get_tools && make get_vendor_deps` from the root of
this repository, run `make build` here to build the `basecoind` and `basecli`
binaries.
If you want to create a new application, start by copying the Basecoin app.
# Building your own Blockchain
Basecoin is the equivalent of an ERC20 token contract for blockchains. In order
to deploy your own application all you need to do is clone `examples/basecoin`
and run it. Now you are already running your own blockchain. In the following
I will explain how to add functionality to your blockchain. This is akin to
defining your own vesting schedule within a contract or setting a specific
multisig. You are just extending the base layer with extra functionality here
and there.
## Structure of Basecoin
Basecoin is build with the cosmos-sdk. It is a sample application that works
with any engine that implements the ABCI protocol. Basecoin defines multiple
unique modules as well as uses modules directly from the sdk. If you want
to modify Basecoin, you either remove or add modules according to your wishes.
## Modules
A module is a fundamental unit in the cosmos-sdk. A module defines its own
transaction, handles its own state as well as its own state transition logic.
Globally, in the `app/app.go` file you just have to define a key for that
module to access some parts of the state, as well as initialise the module
object and finally add it to the transaction router. The router ensures that
every module only gets its own messages.
## Transactions
A user can send a transaction to the running blockchain application. This
transaction can be of any of the ones that are supported by any of the
registered modules.
### CheckTx
Once a user has submitted their transaction to the engine,
the engine will first run `checkTx` to confirm that it is a valid transaction.
The module has to define a handler that knows how to handle every transaction
type. The corresponding handler gets invoked with the checkTx flag set to true.
This means that the handler shouldn't do any expensive operations, but it can
and should write to the checkTx state.
### DeliverTx
The engine calls `deliverTx` when a new block has been agreed upon in
consensus. Again, the corresponding module will have its handler invoked
and the state and context is passed in. During deliverTx execution the
transaction needs to be processed fully and the results are written to the
application state.
## CLI
The cosmos-sdk contains a number of helper libraries in `clients/` to build cli
and RPC interfaces for your specific application.

View File

@ -15,11 +15,9 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/simplestake"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/sketchy"
)
const (
@ -33,6 +31,7 @@ type BasecoinApp struct {
// keys to access the substores
capKeyMainStore *sdk.KVStoreKey
capKeyAccountStore *sdk.KVStoreKey
capKeyIBCStore *sdk.KVStoreKey
capKeyStakingStore *sdk.KVStoreKey
@ -40,14 +39,15 @@ type BasecoinApp struct {
accountMapper sdk.AccountMapper
}
func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
func NewBasecoinApp(logger log.Logger, dbs map[string]dbm.DB) *BasecoinApp {
// create your application object
var app = &BasecoinApp{
BaseApp: bam.NewBaseApp(appName, logger, db),
BaseApp: bam.NewBaseApp(appName, logger, dbs["main"]),
cdc: MakeCodec(),
capKeyMainStore: sdk.NewKVStoreKey("main"),
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
capKeyStakingStore: sdk.NewKVStoreKey("staking"),
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
}
// define the accountMapper
@ -58,20 +58,22 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
// add handlers
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
stakeKeeper := staking.NewKeeper(app.capKeyStakingStore, coinKeeper)
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
app.Router().
AddRoute("bank", bank.NewHandler(coinKeeper), nil).
AddRoute("cool", cool.NewHandler(coolKeeper), coolKeeper.InitGenesis).
AddRoute("sketchy", sketchy.NewHandler(), nil).
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper), nil).
AddRoute("staking", staking.NewHandler(stakeKeeper), nil)
AddRoute("bank", bank.NewHandler(coinKeeper)).
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
// initialize BaseApp
app.SetTxDecoder(app.txDecoder)
app.SetInitChainer(app.initChainer)
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyIBCStore, app.capKeyStakingStore)
app.MountStoreWithDB(app.capKeyMainStore, sdk.StoreTypeIAVL, dbs["main"])
app.MountStoreWithDB(app.capKeyAccountStore, sdk.StoreTypeIAVL, dbs["acc"])
app.MountStoreWithDB(app.capKeyIBCStore, sdk.StoreTypeIAVL, dbs["ibc"])
app.MountStoreWithDB(app.capKeyStakingStore, sdk.StoreTypeIAVL, dbs["staking"])
// NOTE: Broken until #532 lands
//app.MountStoresIAVL(app.capKeyMainStore, app.capKeyIBCStore, app.capKeyStakingStore)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper))
err := app.LoadLatestVersion(app.capKeyMainStore)
if err != nil {
@ -96,12 +98,10 @@ func MakeCodec() *wire.Codec {
struct{ sdk.Msg }{},
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
oldwire.ConcreteType{ibc.IBCTransferMsg{}, msgTypeIBCTransferMsg},
oldwire.ConcreteType{ibc.IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
oldwire.ConcreteType{staking.BondMsg{}, msgTypeBondMsg},
oldwire.ConcreteType{staking.UnbondMsg{}, msgTypeUnbondMsg},
oldwire.ConcreteType{simplestake.BondMsg{}, msgTypeBondMsg},
oldwire.ConcreteType{simplestake.UnbondMsg{}, msgTypeUnbondMsg},
)
const accTypeApp = 0x1

View File

@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
@ -26,50 +25,103 @@ import (
var (
chainID = "" // TODO
accName = "foobart"
priv1 = crypto.GenPrivKeyEd25519()
addr1 = priv1.PubKey().Address()
addr2 = crypto.GenPrivKeyEd25519().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,
}
sendMsg = bank.SendMsg{
sendMsg1 = bank.SendMsg{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
}
quizMsg1 = cool.QuizMsg{
Sender: addr1,
CoolAnswer: "icecold",
sendMsg2 = bank.SendMsg{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{
bank.NewOutput(addr2, halfCoins),
bank.NewOutput(addr3, halfCoins),
},
}
quizMsg2 = cool.QuizMsg{
Sender: addr1,
CoolAnswer: "badvibesonly",
sendMsg3 = bank.SendMsg{
Inputs: []bank.Input{
bank.NewInput(addr1, coins),
bank.NewInput(addr4, coins),
},
Outputs: []bank.Output{
bank.NewOutput(addr2, coins),
bank.NewOutput(addr3, coins),
},
}
setTrendMsg1 = cool.SetTrendMsg{
Sender: addr1,
Cool: "icecold",
sendMsg4 = bank.SendMsg{
Inputs: []bank.Input{
bank.NewInput(addr2, coins),
},
Outputs: []bank.Output{
bank.NewOutput(addr1, coins),
},
}
setTrendMsg2 = cool.SetTrendMsg{
Sender: addr1,
Cool: "badvibesonly",
}
setTrendMsg3 = cool.SetTrendMsg{
Sender: addr1,
Cool: "warmandkind",
sendMsg5 = bank.SendMsg{
Inputs: []bank.Input{
bank.NewInput(addr1, manyCoins),
},
Outputs: []bank.Output{
bank.NewOutput(addr2, manyCoins),
},
}
)
func newBasecoinApp() *BasecoinApp {
func loggerAndDBs() (log.Logger, map[string]dbm.DB) {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
db := dbm.NewMemDB()
return NewBasecoinApp(logger, db)
dbs := map[string]dbm.DB{
"main": dbm.NewMemDB(),
"acc": dbm.NewMemDB(),
"ibc": dbm.NewMemDB(),
"staking": dbm.NewMemDB(),
}
return logger, dbs
}
func newBasecoinApp() *BasecoinApp {
logger, dbs := loggerAndDBs()
return NewBasecoinApp(logger, dbs)
}
func setGenesisAccounts(bapp *BasecoinApp, accs ...auth.BaseAccount) error {
genaccs := make([]*types.GenesisAccount, len(accs))
for i, acc := range accs {
genaccs[i] = types.NewGenesisAccount(&types.AppAccount{acc, accName})
}
genesisState := types.GenesisState{
Accounts: genaccs,
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
if err != nil {
return err
}
// Initialize the chain
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
return nil
}
//_______________________________________________________________________
@ -80,41 +132,60 @@ func TestMsgs(t *testing.T) {
msgs := []struct {
msg sdk.Msg
}{
{sendMsg},
{quizMsg1},
{setTrendMsg1},
{sendMsg1},
}
sequences := []int64{0}
for i, m := range msgs {
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, m.msg))
tx := sdk.NewStdTx(m.msg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
}})
// just marshal/unmarshal!
cdc := MakeCodec()
txBytes, err := cdc.MarshalBinary(tx)
require.NoError(t, err, "i: %v", i)
// Run a Check
cres := bapp.CheckTx(txBytes)
assert.Equal(t, sdk.CodeUnknownAddress,
sdk.CodeType(cres.Code), "i: %v, log: %v", i, cres.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
dres := bapp.DeliverTx(txBytes)
assert.Equal(t, sdk.CodeUnknownAddress,
sdk.CodeType(dres.Code), "i: %v, log: %v", i, dres.Log)
// Run a CheckDeliver
SignCheckDeliver(t, bapp, m.msg, []int64{int64(i)}, false, priv1)
}
}
func TestSortGenesis(t *testing.T) {
logger, dbs := loggerAndDBs()
bapp := NewBasecoinApp(logger, dbs)
// Note the order: the coins are unsorted!
coinDenom1, coinDenom2 := "foocoin", "barcoin"
genState := fmt.Sprintf(`{
"accounts": [{
"address": "%s",
"coins": [
{
"denom": "%s",
"amount": 10
},
{
"denom": "%s",
"amount": 20
}
]
}]
}`, addr1.String(), coinDenom1, coinDenom2)
// Initialize the chain
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, []byte(genState)})
bapp.Commit()
// Unsorted coins means invalid
err := sendMsg5.ValidateBasic()
require.Equal(t, sdk.CodeInvalidCoins, err.ABCICode(), err.ABCILog())
// Sort coins, should be valid
sendMsg5.Inputs[0].Coins.Sort()
sendMsg5.Outputs[0].Coins.Sort()
err = sendMsg5.ValidateBasic()
require.Nil(t, err)
// Ensure we can send
SignCheckDeliver(t, bapp, sendMsg5, []int64{0}, true, priv1)
}
func TestGenesis(t *testing.T) {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
db := dbm.NewMemDB()
bapp := NewBasecoinApp(logger, db)
logger, dbs := loggerAndDBs()
bapp := NewBasecoinApp(logger, dbs)
// Construct some genesis bytes to reflect basecoin/types/AppAccount
pk := crypto.GenPrivKeyEd25519().PubKey()
@ -127,31 +198,19 @@ func TestGenesis(t *testing.T) {
}
acc := &types.AppAccount{baseAcc, "foobart"}
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
err = setGenesisAccounts(bapp, baseAcc)
assert.Nil(t, err)
// A checkTx context
ctx := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)
/*
// reload app and ensure the account is still there
bapp = NewBasecoinApp(logger, db)
bapp = NewBasecoinApp(logger, dbs)
ctx = bapp.BaseApp.NewContext(true, abci.Header{})
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)
*/
}
func TestSendMsgWithAccounts(t *testing.T) {
@ -165,69 +224,124 @@ func TestSendMsgWithAccounts(t *testing.T) {
Address: addr1,
Coins: coins,
}
acc1 := &types.AppAccount{baseAcc, "foobart"}
// Construct genesis state
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc1),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
// Initialize the chain
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
err = setGenesisAccounts(bapp, baseAcc)
assert.Nil(t, err)
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
assert.Equal(t, acc1, res1)
assert.Equal(t, baseAcc, res1.(*types.AppAccount).BaseAccount)
// Sign the tx
sequences := []int64{0}
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, sendMsg))
tx := sdk.NewStdTx(sendMsg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
}})
// Run a Check
res := bapp.Check(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
// Run a CheckDeliver
SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, true, priv1)
// Check balances
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
res3 := bapp.accountMapper.GetAccount(ctxDeliver, addr2)
assert.Equal(t, fmt.Sprintf("%v", res2.GetCoins()), "67foocoin")
assert.Equal(t, fmt.Sprintf("%v", res3.GetCoins()), "10foocoin")
CheckBalance(t, bapp, addr1, "67foocoin")
CheckBalance(t, bapp, addr2, "10foocoin")
// Delivering again should cause replay error
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeInvalidSequence, res.Code, res.Log)
SignCheckDeliver(t, bapp, 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 = bapp.Deliver(tx)
res := bapp.Deliver(tx)
assert.Equal(t, sdk.CodeUnauthorized, 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)
SignCheckDeliver(t, bapp, sendMsg1, []int64{1}, true, priv1)
}
func TestSendMsgMultipleOut(t *testing.T) {
bapp := newBasecoinApp()
genCoins, err := sdk.ParseCoins("42foocoin")
require.Nil(t, err)
acc1 := auth.BaseAccount{
Address: addr1,
Coins: genCoins,
}
acc2 := auth.BaseAccount{
Address: addr2,
Coins: genCoins,
}
err = setGenesisAccounts(bapp, acc1, acc2)
assert.Nil(t, err)
// Simulate a Block
SignCheckDeliver(t, bapp, sendMsg2, []int64{0}, true, priv1)
// Check balances
CheckBalance(t, bapp, addr1, "32foocoin")
CheckBalance(t, bapp, addr2, "47foocoin")
CheckBalance(t, bapp, addr3, "5foocoin")
}
func TestSengMsgMultipleInOut(t *testing.T) {
bapp := newBasecoinApp()
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 = setGenesisAccounts(bapp, acc1, acc2, acc4)
assert.Nil(t, err)
// CheckDeliver
SignCheckDeliver(t, bapp, sendMsg3, []int64{0, 0}, true, priv1, priv4)
// Check balances
CheckBalance(t, bapp, addr1, "32foocoin")
CheckBalance(t, bapp, addr4, "32foocoin")
CheckBalance(t, bapp, addr2, "52foocoin")
CheckBalance(t, bapp, addr3, "10foocoin")
}
func TestSendMsgDependent(t *testing.T) {
bapp := newBasecoinApp()
genCoins, err := sdk.ParseCoins("42foocoin")
require.Nil(t, err)
acc1 := auth.BaseAccount{
Address: addr1,
Coins: genCoins,
}
err = setGenesisAccounts(bapp, acc1)
assert.Nil(t, err)
// CheckDeliver
SignCheckDeliver(t, bapp, sendMsg1, []int64{0}, true, priv1)
// Check balances
CheckBalance(t, bapp, addr1, "32foocoin")
CheckBalance(t, bapp, addr2, "10foocoin")
// Simulate a Block
SignCheckDeliver(t, bapp, sendMsg4, []int64{0}, true, priv2)
// Check balances
CheckBalance(t, bapp, addr1, "42foocoin")
}
func TestQuizMsg(t *testing.T) {
@ -247,9 +361,6 @@ func TestQuizMsg(t *testing.T) {
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc1),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
@ -264,48 +375,22 @@ func TestQuizMsg(t *testing.T) {
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
assert.Equal(t, acc1, res1)
// Set the trend, submit a really cool quiz and check for reward
SignCheckDeliver(t, bapp, setTrendMsg1, 0, true)
SignCheckDeliver(t, bapp, quizMsg1, 1, true)
CheckBalance(t, bapp, "69icecold")
SignCheckDeliver(t, bapp, quizMsg2, 2, false) // result without reward
CheckBalance(t, bapp, "69icecold")
SignCheckDeliver(t, bapp, quizMsg1, 3, true)
CheckBalance(t, bapp, "138icecold")
SignCheckDeliver(t, bapp, setTrendMsg2, 4, true) // reset the trend
SignCheckDeliver(t, bapp, quizMsg1, 5, false) // the same answer will nolonger do!
CheckBalance(t, bapp, "138icecold")
SignCheckDeliver(t, bapp, quizMsg2, 6, true) // earlier answer now relavent again
CheckBalance(t, bapp, "69badvibesonly,138icecold")
SignCheckDeliver(t, bapp, setTrendMsg3, 7, false) // expect to fail to set the trend to something which is not cool
}
func TestHandler(t *testing.T) {
func TestIBCMsgs(t *testing.T) {
bapp := newBasecoinApp()
sourceChain := "source-chain"
destChain := "dest-chain"
vals := []abci.Validator{}
baseAcc := auth.BaseAccount{
Address: addr1,
Coins: coins,
}
acc1 := &types.AppAccount{baseAcc, "foobart"}
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc1),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
err := setGenesisAccounts(bapp, baseAcc)
assert.Nil(t, err)
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
@ -329,24 +414,32 @@ func TestHandler(t *testing.T) {
Sequence: 0,
}
SignCheckDeliver(t, bapp, transferMsg, 0, true)
CheckBalance(t, bapp, "")
SignCheckDeliver(t, bapp, transferMsg, 1, false)
SignCheckDeliver(t, bapp, receiveMsg, 2, true)
CheckBalance(t, bapp, "10foocoin")
SignCheckDeliver(t, bapp, receiveMsg, 3, false)
SignCheckDeliver(t, bapp, transferMsg, []int64{0}, true, priv1)
CheckBalance(t, bapp, addr1, "")
SignCheckDeliver(t, bapp, transferMsg, []int64{1}, false, priv1)
SignCheckDeliver(t, bapp, receiveMsg, []int64{2}, true, priv1)
CheckBalance(t, bapp, addr1, "10foocoin")
SignCheckDeliver(t, bapp, receiveMsg, []int64{3}, false, priv1)
}
// TODO describe the use of this function
func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq int64, expPass bool) {
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, bapp *BasecoinApp, msg sdk.Msg, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) {
// Sign the tx
tx := sdk.NewStdTx(msg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: priv1.Sign(sdk.StdSignBytes(chainID, []int64{seq}, fee, msg)),
Sequence: seq,
}})
tx := genTx(msg, seq, priv...)
// Run a Check
res := bapp.Check(tx)
if expPass {
@ -367,8 +460,8 @@ func SignCheckDeliver(t *testing.T, bapp *BasecoinApp, msg sdk.Msg, seq int64, e
//bapp.Commit()
}
func CheckBalance(t *testing.T, bapp *BasecoinApp, balExpected string) {
func CheckBalance(t *testing.T, bapp *BasecoinApp, addr sdk.Address, balExpected string) {
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr)
assert.Equal(t, balExpected, fmt.Sprintf("%v", res2.GetCoins()))
}

View File

@ -1,10 +1,10 @@
package main
import (
"errors"
"github.com/spf13/cobra"
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client"
@ -13,29 +13,24 @@ import (
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
coolcmd "github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool/commands"
"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"
stakingcmd "github.com/cosmos/cosmos-sdk/x/staking/commands"
simplestakingcmd "github.com/cosmos/cosmos-sdk/x/simplestake/commands"
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
)
// gaiacliCmd is the entry point for this binary
// rootCmd is the entry point for this binary
var (
basecliCmd = &cobra.Command{
rootCmd = &cobra.Command{
Use: "basecli",
Short: "Basecoin light-client",
}
)
func todoNotImplemented(_ *cobra.Command, _ []string) error {
return errors.New("TODO: Command not yet implemented")
}
func main() {
// disable sorting
cobra.EnableCommandSorting = false
@ -48,44 +43,36 @@ func main() {
// with the cdc
// add standard rpc, and tx commands
rpc.AddCommands(basecliCmd)
basecliCmd.AddCommand(client.LineBreak)
tx.AddCommands(basecliCmd, cdc)
basecliCmd.AddCommand(client.LineBreak)
rpc.AddCommands(rootCmd)
rootCmd.AddCommand(client.LineBreak)
tx.AddCommands(rootCmd, cdc)
rootCmd.AddCommand(client.LineBreak)
// add query/post commands (custom to binary)
basecliCmd.AddCommand(
rootCmd.AddCommand(
client.GetCommands(
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
)...)
basecliCmd.AddCommand(
rootCmd.AddCommand(
client.PostCommands(
bankcmd.SendTxCmd(cdc),
)...)
basecliCmd.AddCommand(
client.PostCommands(
coolcmd.QuizTxCmd(cdc),
)...)
basecliCmd.AddCommand(
client.PostCommands(
coolcmd.SetTrendTxCmd(cdc),
)...)
basecliCmd.AddCommand(
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCTransferCmd(cdc),
)...)
basecliCmd.AddCommand(
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCRelayCmd(cdc),
stakingcmd.BondTxCmd(cdc),
simplestakingcmd.BondTxCmd(cdc),
)...)
basecliCmd.AddCommand(
rootCmd.AddCommand(
client.PostCommands(
stakingcmd.UnbondTxCmd(cdc),
simplestakingcmd.UnbondTxCmd(cdc),
)...)
// add proxy, version and key info
basecliCmd.AddCommand(
rootCmd.AddCommand(
client.LineBreak,
lcd.ServeCommand(cdc),
keys.Commands(),
@ -94,6 +81,6 @@ func main() {
)
// prepare and add flags
executor := cli.PrepareMainCmd(basecliCmd, "BC", os.ExpandEnv("$HOME/.basecli"))
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.basecli"))
executor.Execute()
}

View File

@ -1,8 +1,6 @@
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
@ -15,66 +13,51 @@ import (
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/version"
)
// basecoindCmd is the entry point for this binary
// rootCmd is the entry point for this binary
var (
basecoindCmd = &cobra.Command{
Use: "gaiad",
Short: "Gaia Daemon (server)",
context = server.NewDefaultContext()
rootCmd = &cobra.Command{
Use: "basecoind",
Short: "Basecoin Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(context),
}
)
// defaultOptions sets up the app_options for the
// default genesis file
func defaultOptions(args []string) (json.RawMessage, error) {
addr, secret, err := server.GenerateCoinKey()
if err != nil {
return nil, err
}
fmt.Println("Secret phrase to access coins:")
fmt.Println(secret)
opts := fmt.Sprintf(`{
"accounts": [{
"address": "%s",
"coins": [
{
"denom": "mycoin",
"amount": 9007199254740992
}
]
}]
}`, addr)
return json.RawMessage(opts), nil
}
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
db, err := dbm.NewGoLevelDB("basecoin", filepath.Join(rootDir, "data"))
dataDir := filepath.Join(rootDir, "data")
dbMain, err := dbm.NewGoLevelDB("basecoin", dataDir)
if err != nil {
return nil, err
}
bapp := app.NewBasecoinApp(logger, db)
dbAcc, err := dbm.NewGoLevelDB("basecoin-acc", dataDir)
if err != nil {
return nil, err
}
dbIBC, err := dbm.NewGoLevelDB("basecoin-ibc", dataDir)
if err != nil {
return nil, err
}
dbStaking, err := dbm.NewGoLevelDB("basecoin-staking", dataDir)
if err != nil {
return nil, err
}
dbs := map[string]dbm.DB{
"main": dbMain,
"acc": dbAcc,
"ibc": dbIBC,
"staking": dbStaking,
}
bapp := app.NewBasecoinApp(logger, dbs)
return bapp, nil
}
func main() {
// TODO: set logger through CLI
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
With("module", "main")
basecoindCmd.AddCommand(
server.InitCmd(defaultOptions, logger),
server.StartCmd(generateApp, logger),
server.UnsafeResetAllCmd(logger),
server.ShowNodeIdCmd(logger),
server.ShowValidatorCmd(logger),
version.VersionCmd,
)
server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context)
// prepare and add flags
rootDir := os.ExpandEnv("$HOME/.basecoind")
executor := cli.PrepareBaseCmd(basecoindCmd, "BC", rootDir)
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
executor.Execute()
}

View File

@ -55,7 +55,7 @@ func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
return &GenesisAccount{
Name: aa.Name,
Address: aa.Address,
Coins: aa.Coins,
Coins: aa.Coins.Sort(),
}
}
@ -63,7 +63,7 @@ func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) {
baseAcc := auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins,
Coins: ga.Coins.Sort(),
}
return &AppAccount{
BaseAccount: baseAcc,

View File

@ -0,0 +1,189 @@
package app
import (
"encoding/json"
abci "github.com/tendermint/abci/types"
oldwire "github.com/tendermint/go-wire"
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/simplestake"
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/sketchy"
)
const (
appName = "DemocoinApp"
)
// Extended ABCI application
type DemocoinApp struct {
*bam.BaseApp
cdc *wire.Codec
// keys to access the substores
capKeyMainStore *sdk.KVStoreKey
capKeyAccountStore *sdk.KVStoreKey
capKeyPowStore *sdk.KVStoreKey
capKeyIBCStore *sdk.KVStoreKey
capKeyStakingStore *sdk.KVStoreKey
// Manage getting and setting accounts
accountMapper sdk.AccountMapper
}
func NewDemocoinApp(logger log.Logger, dbs map[string]dbm.DB) *DemocoinApp {
// create your application object
var app = &DemocoinApp{
BaseApp: bam.NewBaseApp(appName, logger, dbs["main"]),
cdc: MakeCodec(),
capKeyMainStore: sdk.NewKVStoreKey("main"),
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
capKeyPowStore: sdk.NewKVStoreKey("pow"),
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
}
// define the accountMapper
app.accountMapper = auth.NewAccountMapperSealed(
app.capKeyMainStore, // target store
&types.AppAccount{}, // prototype
)
// add handlers
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper)
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
app.Router().
AddRoute("bank", bank.NewHandler(coinKeeper)).
AddRoute("cool", cool.NewHandler(coolKeeper)).
AddRoute("pow", powKeeper.Handler).
AddRoute("sketchy", sketchy.NewHandler()).
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
// initialize BaseApp
app.SetTxDecoder(app.txDecoder)
app.SetInitChainer(app.initChainerFn(coolKeeper, powKeeper))
app.MountStoreWithDB(app.capKeyMainStore, sdk.StoreTypeIAVL, dbs["main"])
app.MountStoreWithDB(app.capKeyAccountStore, sdk.StoreTypeIAVL, dbs["acc"])
app.MountStoreWithDB(app.capKeyPowStore, sdk.StoreTypeIAVL, dbs["pow"])
app.MountStoreWithDB(app.capKeyIBCStore, sdk.StoreTypeIAVL, dbs["ibc"])
app.MountStoreWithDB(app.capKeyStakingStore, sdk.StoreTypeIAVL, dbs["staking"])
// NOTE: Broken until #532 lands
//app.MountStoresIAVL(app.capKeyMainStore, app.capKeyIBCStore, app.capKeyStakingStore)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper))
err := app.LoadLatestVersion(app.capKeyMainStore)
if err != nil {
cmn.Exit(err.Error())
}
return app
}
// custom tx codec
// TODO: use new go-wire
func MakeCodec() *wire.Codec {
const msgTypeSend = 0x1
const msgTypeIssue = 0x2
const msgTypeQuiz = 0x3
const msgTypeSetTrend = 0x4
const msgTypeMine = 0x5
const msgTypeIBCTransferMsg = 0x6
const msgTypeIBCReceiveMsg = 0x7
const msgTypeBondMsg = 0x8
const msgTypeUnbondMsg = 0x9
var _ = oldwire.RegisterInterface(
struct{ sdk.Msg }{},
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
oldwire.ConcreteType{pow.MineMsg{}, msgTypeMine},
oldwire.ConcreteType{ibc.IBCTransferMsg{}, msgTypeIBCTransferMsg},
oldwire.ConcreteType{ibc.IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
oldwire.ConcreteType{simplestake.BondMsg{}, msgTypeBondMsg},
oldwire.ConcreteType{simplestake.UnbondMsg{}, msgTypeUnbondMsg},
)
const accTypeApp = 0x1
var _ = oldwire.RegisterInterface(
struct{ sdk.Account }{},
oldwire.ConcreteType{&types.AppAccount{}, accTypeApp},
)
cdc := wire.NewCodec()
// cdc.RegisterInterface((*sdk.Msg)(nil), nil)
// bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types.
// crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types.
// ibc.RegisterWire(cdc) // Register ibc.[IBCTransferMsg, IBCReceiveMsg] types.
return cdc
}
// custom logic for transaction decoding
func (app *DemocoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx = sdk.StdTx{}
if len(txBytes) == 0 {
return nil, sdk.ErrTxDecode("txBytes are empty")
}
// StdTx.Msg is an interface. The concrete types
// are registered by MakeTxCodec in bank.RegisterWire.
err := app.cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxDecode("").TraceCause(err, "")
}
return tx, nil
}
// custom logic for democoin initialization
func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keeper) sdk.InitChainer {
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
stateJSON := req.AppStateBytes
genesisState := new(types.GenesisState)
err := json.Unmarshal(stateJSON, genesisState)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
}
for _, gacc := range genesisState.Accounts {
acc, err := gacc.ToAppAccount()
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
}
app.accountMapper.SetAccount(ctx, acc)
}
// Application specific genesis handling
err = coolKeeper.InitGenesis(ctx, genesisState.CoolGenesis)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
}
err = powKeeper.InitGenesis(ctx, genesisState.PowGenesis)
if err != nil {
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
// return sdk.ErrGenesisParse("").TraceCause(err, "")
}
return abci.ResponseInitChain{}
}
}

View File

@ -0,0 +1,436 @@
package app
import (
"encoding/json"
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
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"
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
priv1 = crypto.GenPrivKeyEd25519()
addr1 = priv1.PubKey().Address()
addr2 = crypto.GenPrivKeyEd25519().PubKey().Address()
coins = sdk.Coins{{"foocoin", 10}}
fee = sdk.StdFee{
sdk.Coins{{"foocoin", 0}},
0,
}
sendMsg = bank.SendMsg{
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
}
quizMsg1 = cool.QuizMsg{
Sender: addr1,
CoolAnswer: "icecold",
}
quizMsg2 = cool.QuizMsg{
Sender: addr1,
CoolAnswer: "badvibesonly",
}
setTrendMsg1 = cool.SetTrendMsg{
Sender: addr1,
Cool: "icecold",
}
setTrendMsg2 = cool.SetTrendMsg{
Sender: addr1,
Cool: "badvibesonly",
}
setTrendMsg3 = cool.SetTrendMsg{
Sender: addr1,
Cool: "warmandkind",
}
)
func loggerAndDBs() (log.Logger, map[string]dbm.DB) {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
dbs := map[string]dbm.DB{
"main": dbm.NewMemDB(),
"acc": dbm.NewMemDB(),
"pow": dbm.NewMemDB(),
"ibc": dbm.NewMemDB(),
"staking": dbm.NewMemDB(),
}
return logger, dbs
}
func newDemocoinApp() *DemocoinApp {
logger, dbs := loggerAndDBs()
return NewDemocoinApp(logger, dbs)
}
//_______________________________________________________________________
func TestMsgs(t *testing.T) {
bapp := newDemocoinApp()
msgs := []struct {
msg sdk.Msg
}{
{sendMsg},
{quizMsg1},
{setTrendMsg1},
}
sequences := []int64{0}
for i, m := range msgs {
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, m.msg))
tx := sdk.NewStdTx(m.msg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
}})
// just marshal/unmarshal!
cdc := MakeCodec()
txBytes, err := cdc.MarshalBinary(tx)
require.NoError(t, err, "i: %v", i)
// Run a Check
cres := bapp.CheckTx(txBytes)
assert.Equal(t, sdk.CodeUnknownAddress,
sdk.CodeType(cres.Code), "i: %v, log: %v", i, cres.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
dres := bapp.DeliverTx(txBytes)
assert.Equal(t, sdk.CodeUnknownAddress,
sdk.CodeType(dres.Code), "i: %v, log: %v", i, dres.Log)
}
}
func TestGenesis(t *testing.T) {
logger, dbs := loggerAndDBs()
bapp := NewDemocoinApp(logger, dbs)
// Construct some genesis bytes to reflect democoin/types/AppAccount
pk := crypto.GenPrivKeyEd25519().PubKey()
addr := pk.Address()
coins, err := sdk.ParseCoins("77foocoin,99barcoin")
require.Nil(t, err)
baseAcc := auth.BaseAccount{
Address: addr,
Coins: coins,
}
acc := &types.AppAccount{baseAcc, "foobart"}
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
// A checkTx context
ctx := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)
// reload app and ensure the account is still there
bapp = NewDemocoinApp(logger, dbs)
ctx = bapp.BaseApp.NewContext(true, abci.Header{})
res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
assert.Equal(t, acc, res1)
}
func TestSendMsgWithAccounts(t *testing.T) {
bapp := newDemocoinApp()
// Construct some genesis bytes to reflect democoin/types/AppAccount
// Give 77 foocoin to the first key
coins, err := sdk.ParseCoins("77foocoin")
require.Nil(t, err)
baseAcc := auth.BaseAccount{
Address: addr1,
Coins: coins,
}
acc1 := &types.AppAccount{baseAcc, "foobart"}
// Construct genesis state
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc1),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
// Initialize the chain
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
assert.Equal(t, acc1, res1)
// Sign the tx
sequences := []int64{0}
sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, fee, sendMsg))
tx := sdk.NewStdTx(sendMsg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
}})
// Run a Check
res := bapp.Check(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
// Check balances
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
res3 := bapp.accountMapper.GetAccount(ctxDeliver, addr2)
assert.Equal(t, fmt.Sprintf("%v", res2.GetCoins()), "67foocoin")
assert.Equal(t, fmt.Sprintf("%v", res3.GetCoins()), "10foocoin")
// Delivering again should cause replay error
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeInvalidSequence, 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)
// 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)
}
func TestMineMsg(t *testing.T) {
bapp := newDemocoinApp()
// Construct genesis state
// Construct some genesis bytes to reflect democoin/types/AppAccount
coins := sdk.Coins{}
baseAcc := auth.BaseAccount{
Address: addr1,
Coins: coins,
}
acc1 := &types.AppAccount{baseAcc, "foobart"}
// Construct genesis state
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc1),
},
"cool": map[string]string{
"trend": "ice-cold",
},
"pow": map[string]uint64{
"difficulty": 1,
"count": 0,
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
// Initialize the chain (nil)
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
assert.Equal(t, acc1, res1)
// Mine and check for reward
mineMsg1 := pow.GenerateMineMsg(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)
SignCheckDeliver(t, bapp, mineMsg2, 1, true)
CheckBalance(t, bapp, "2pow")
// Mine again - should be invalid
SignCheckDeliver(t, bapp, mineMsg2, 1, false)
CheckBalance(t, bapp, "2pow")
}
func TestQuizMsg(t *testing.T) {
bapp := newDemocoinApp()
// Construct genesis state
// Construct some genesis bytes to reflect democoin/types/AppAccount
coins := sdk.Coins{}
baseAcc := auth.BaseAccount{
Address: addr1,
Coins: coins,
}
acc1 := &types.AppAccount{baseAcc, "foobart"}
// Construct genesis state
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc1),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
// Initialize the chain (nil)
vals := []abci.Validator{}
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
assert.Equal(t, acc1, res1)
// Set the trend, submit a really cool quiz and check for reward
SignCheckDeliver(t, bapp, setTrendMsg1, 0, true)
SignCheckDeliver(t, bapp, quizMsg1, 1, true)
CheckBalance(t, bapp, "69icecold")
SignCheckDeliver(t, bapp, quizMsg2, 2, false) // result without reward
CheckBalance(t, bapp, "69icecold")
SignCheckDeliver(t, bapp, quizMsg1, 3, true)
CheckBalance(t, bapp, "138icecold")
SignCheckDeliver(t, bapp, setTrendMsg2, 4, true) // reset the trend
SignCheckDeliver(t, bapp, quizMsg1, 5, false) // the same answer will nolonger do!
CheckBalance(t, bapp, "138icecold")
SignCheckDeliver(t, bapp, quizMsg2, 6, true) // earlier answer now relavent again
CheckBalance(t, bapp, "69badvibesonly,138icecold")
SignCheckDeliver(t, bapp, setTrendMsg3, 7, false) // expect to fail to set the trend to something which is not cool
}
func TestHandler(t *testing.T) {
bapp := newDemocoinApp()
sourceChain := "source-chain"
destChain := "dest-chain"
vals := []abci.Validator{}
baseAcc := auth.BaseAccount{
Address: addr1,
Coins: coins,
}
acc1 := &types.AppAccount{baseAcc, "foobart"}
genesisState := map[string]interface{}{
"accounts": []*types.GenesisAccount{
types.NewGenesisAccount(acc1),
},
"cool": map[string]string{
"trend": "ice-cold",
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
bapp.Commit()
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
assert.Equal(t, acc1, 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, bapp, transferMsg, 0, true)
CheckBalance(t, bapp, "")
SignCheckDeliver(t, bapp, transferMsg, 1, false)
SignCheckDeliver(t, bapp, receiveMsg, 2, true)
CheckBalance(t, bapp, "10foocoin")
SignCheckDeliver(t, bapp, receiveMsg, 3, false)
}
// TODO describe the use of this function
func SignCheckDeliver(t *testing.T, bapp *DemocoinApp, msg sdk.Msg, seq int64, expPass bool) {
// Sign the tx
tx := sdk.NewStdTx(msg, fee, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: priv1.Sign(sdk.StdSignBytes(chainID, []int64{seq}, fee, msg)),
Sequence: seq,
}})
// Run a Check
res := bapp.Check(tx)
if expPass {
require.Equal(t, sdk.CodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.CodeOK, 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)
} else {
require.NotEqual(t, sdk.CodeOK, res.Code, res.Log)
}
bapp.EndBlock(abci.RequestEndBlock{})
//bapp.Commit()
}
func CheckBalance(t *testing.T, bapp *DemocoinApp, balExpected string) {
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
assert.Equal(t, balExpected, fmt.Sprintf("%v", res2.GetCoins()))
}

View File

@ -0,0 +1,86 @@
package main
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tmlibs/cli"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
"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"
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
)
// rootCmd is the entry point for this binary
var (
rootCmd = &cobra.Command{
Use: "democli",
Short: "Democoin light-client",
}
)
func main() {
// disable sorting
cobra.EnableCommandSorting = false
// get the codec
cdc := app.MakeCodec()
// TODO: setup keybase, viper object, etc. to be passed into
// the below functions and eliminate global vars, like we do
// with the cdc
// add standard rpc, and tx commands
rpc.AddCommands(rootCmd)
rootCmd.AddCommand(client.LineBreak)
tx.AddCommands(rootCmd, cdc)
rootCmd.AddCommand(client.LineBreak)
// add query/post commands (custom to binary)
rootCmd.AddCommand(
client.GetCommands(
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
)...)
rootCmd.AddCommand(
client.PostCommands(
bankcmd.SendTxCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCTransferCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCRelayCmd(cdc),
simplestakingcmd.BondTxCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
simplestakingcmd.UnbondTxCmd(cdc),
)...)
// add proxy, version and key info
rootCmd.AddCommand(
client.LineBreak,
lcd.ServeCommand(cdc),
keys.Commands(),
client.LineBreak,
version.VersionCmd,
)
// prepare and add flags
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.democli"))
executor.Execute()
}

View File

@ -0,0 +1,88 @@
package main
import (
"encoding/json"
"os"
"path/filepath"
"github.com/spf13/cobra"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/cli"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// rootCmd is the entry point for this binary
var (
context = server.NewDefaultContext()
rootCmd = &cobra.Command{
Use: "democoind",
Short: "Democoin Daemon (server)",
PersistentPreRunE: server.PersistentPreRunEFn(context),
}
)
// defaultAppState sets up the app_state for the
// default genesis file
func defaultAppState(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
baseJSON, err := server.DefaultGenAppState(args, addr, coinDenom)
if err != nil {
return nil, err
}
var jsonMap map[string]json.RawMessage
err = json.Unmarshal(baseJSON, &jsonMap)
if err != nil {
return nil, err
}
jsonMap["cool"] = json.RawMessage(`{
"trend": "ice-cold"
}`)
bz, err := json.Marshal(jsonMap)
return json.RawMessage(bz), err
}
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
dbMain, err := dbm.NewGoLevelDB("democoin", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbAcc, err := dbm.NewGoLevelDB("democoin-acc", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbPow, err := dbm.NewGoLevelDB("democoin-pow", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbIBC, err := dbm.NewGoLevelDB("democoin-ibc", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbStaking, err := dbm.NewGoLevelDB("democoin-staking", filepath.Join(rootDir, "data"))
if err != nil {
return nil, err
}
dbs := map[string]dbm.DB{
"main": dbMain,
"acc": dbAcc,
"pow": dbPow,
"ibc": dbIBC,
"staking": dbStaking,
}
bapp := app.NewDemocoinApp(logger, dbs)
return bapp, nil
}
func main() {
server.AddCommands(rootCmd, defaultAppState, generateApp, context)
// prepare and add flags
rootDir := os.ExpandEnv("$HOME/.democoind")
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
executor.Execute()
}

View File

@ -0,0 +1,77 @@
package types
import (
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/examples/democoin/x/cool"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
)
var _ sdk.Account = (*AppAccount)(nil)
// Custom extensions for this application. This is just an example of
// extending auth.BaseAccount with custom fields.
//
// This is compatible with the stock auth.AccountStore, since
// auth.AccountStore uses the flexible go-wire library.
type AppAccount struct {
auth.BaseAccount
Name string `json:"name"`
}
// nolint
func (acc AppAccount) GetName() string { return acc.Name }
func (acc *AppAccount) SetName(name string) { acc.Name = name }
// Get the AccountDecoder function for the custom AppAccount
func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder {
return func(accBytes []byte) (res sdk.Account, err error) {
if len(accBytes) == 0 {
return nil, sdk.ErrTxDecode("accBytes are empty")
}
acct := new(AppAccount)
err = cdc.UnmarshalBinary(accBytes, &acct)
if err != nil {
panic(err)
}
return acct, err
}
}
//___________________________________________________________________________________
// State to Unmarshal
type GenesisState struct {
Accounts []*GenesisAccount `json:"accounts"`
PowGenesis pow.PowGenesis `json:"pow"`
CoolGenesis cool.CoolGenesis `json:"cool"`
}
// GenesisAccount doesn't need pubkey or sequence
type GenesisAccount struct {
Name string `json:"name"`
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
}
func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
return &GenesisAccount{
Name: aa.Name,
Address: aa.Address,
Coins: aa.Coins.Sort(),
}
}
// convert GenesisAccount to AppAccount
func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) {
baseAcc := auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins.Sort(),
}
return &AppAccount{
BaseAccount: baseAcc,
Name: ga.Name,
}, nil
}

View File

@ -8,10 +8,10 @@ import (
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
)
// take the coolness quiz transaction
@ -24,8 +24,10 @@ func QuizTxCmd(cdc *wire.Codec) *cobra.Command {
return errors.New("You must provide an answer")
}
ctx := context.NewCoreContextFromViper()
// get the from address from the name flag
from, err := builder.GetFromAddress()
from, err := ctx.GetFromAddress()
if err != nil {
return err
}
@ -37,7 +39,7 @@ func QuizTxCmd(cdc *wire.Codec) *cobra.Command {
name := viper.GetString(client.FlagName)
// build and sign the transaction, then broadcast to Tendermint
res, err := builder.SignBuildBroadcast(name, msg, cdc)
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
if err != nil {
return err
}
@ -58,8 +60,10 @@ func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command {
return errors.New("You must provide an answer")
}
ctx := context.NewCoreContextFromViper()
// get the from address from the name flag
from, err := builder.GetFromAddress()
from, err := ctx.GetFromAddress()
if err != nil {
return err
}
@ -71,7 +75,7 @@ func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command {
msg := cool.NewSetTrendMsg(from, args[0])
// build and sign the transaction, then broadcast to Tendermint
res, err := builder.SignBuildBroadcast(name, msg, cdc)
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
if err != nil {
return err
}

View File

@ -1,17 +1,10 @@
package cool
import (
"encoding/json"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
)
// Cool genesis state, containing the genesis trend
type GenesisState struct {
trend string
}
// Keeper - handlers sets/gets of custom variables for your module
type Keeper struct {
ck bank.CoinKeeper
@ -49,11 +42,7 @@ func (k Keeper) CheckTrend(ctx sdk.Context, guessedTrend string) bool {
}
// InitGenesis - store the genesis trend
func (k Keeper) InitGenesis(ctx sdk.Context, data json.RawMessage) error {
var state GenesisState
if err := json.Unmarshal(data, &state); err != nil {
return err
}
k.setTrend(ctx, state.trend)
func (k Keeper) InitGenesis(ctx sdk.Context, data CoolGenesis) error {
k.setTrend(ctx, data.Trend)
return nil
}

View File

@ -15,6 +15,11 @@ type SetTrendMsg struct {
Cool string
}
// Genesis state - specify genesis trend
type CoolGenesis struct {
Trend string `json:"trend"`
}
// New cool message
func NewSetTrendMsg(sender sdk.Address, cool string) SetTrendMsg {
return SetTrendMsg{

View File

@ -0,0 +1,66 @@
package commands
import (
"fmt"
"strconv"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
"github.com/cosmos/cosmos-sdk/wire"
)
func MineCmd(cdc *wire.Codec) *cobra.Command {
return &cobra.Command{
Use: "mine [difficulty] [count] [nonce] [solution]",
Short: "Mine some coins with proof-of-work!",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 4 {
return errors.New("You must provide a difficulty, a solution, and a nonce (in that order)")
}
// get from address and parse arguments
ctx := context.NewCoreContextFromViper()
from, err := ctx.GetFromAddress()
if err != nil {
return err
}
difficulty, err := strconv.ParseUint(args[0], 0, 64)
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
}
solution := []byte(args[3])
msg := pow.NewMineMsg(from, difficulty, count, nonce, solution)
// get account name
name := ctx.FromAddressName
// build and sign the transaction, then broadcast to Tendermint
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
}

View File

@ -0,0 +1,82 @@
package pow
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
type CodeType = sdk.CodeType
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
)
func codeToDefaultMsg(code CodeType) string {
switch code {
case CodeInvalidDifficulty:
return "Insuffient difficulty"
case CodeNonexistentDifficulty:
return "Nonexistent difficulty"
case CodeNonexistentReward:
return "Nonexistent reward"
case CodeNonexistentCount:
return "Nonexistent count"
case CodeInvalidProof:
return "Invalid proof"
case CodeNotBelowTarget:
return "Not below target"
case CodeInvalidCount:
return "Invalid count"
case CodeUnknownRequest:
return "Unknown request"
default:
return sdk.CodeToDefaultMsg(code)
}
}
func ErrInvalidDifficulty(msg string) sdk.Error {
return newError(CodeInvalidDifficulty, msg)
}
func ErrNonexistentDifficulty() sdk.Error {
return newError(CodeNonexistentDifficulty, "")
}
func ErrNonexistentReward() sdk.Error {
return newError(CodeNonexistentReward, "")
}
func ErrNonexistentCount() sdk.Error {
return newError(CodeNonexistentCount, "")
}
func ErrInvalidProof(msg string) sdk.Error {
return newError(CodeInvalidProof, msg)
}
func ErrNotBelowTarget(msg string) sdk.Error {
return newError(CodeNotBelowTarget, msg)
}
func ErrInvalidCount(msg string) sdk.Error {
return newError(CodeInvalidCount, msg)
}
func msgOrDefaultMsg(msg string, code CodeType) string {
if msg != "" {
return msg
} else {
return codeToDefaultMsg(code)
}
}
func newError(code CodeType, msg string) sdk.Error {
msg = msgOrDefaultMsg(msg, code)
return sdk.NewError(code, msg)
}

View File

@ -0,0 +1,42 @@
package pow
import (
"reflect"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func (pk Keeper) Handler(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case MineMsg:
return handleMineMsg(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 {
// precondition: msg has passed ValidateBasic
newDiff, newCount, err := pk.CheckValid(ctx, msg.Difficulty, msg.Count)
if err != nil {
return err.Result()
}
// commented for now, makes testing difficult
// TODO figure out a better test method that allows early CheckTx return
/*
if ctx.IsCheckTx() {
return sdk.Result{} // TODO
}
*/
err = pk.ApplyValid(ctx, msg.Sender, newDiff, newCount)
if err != nil {
return err.Result()
}
return sdk.Result{}
}

View File

@ -0,0 +1,55 @@
package pow
import (
"testing"
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
auth "github.com/cosmos/cosmos-sdk/x/auth"
bank "github.com/cosmos/cosmos-sdk/x/bank"
)
func TestPowHandler(t *testing.T) {
ms, capKey := setupMultiStore()
am := auth.NewAccountMapper(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)
handler := keeper.Handler
addr := sdk.Address([]byte("sender"))
count := uint64(1)
difficulty := uint64(2)
err := keeper.InitGenesis(ctx, PowGenesis{uint64(1), uint64(0)})
assert.Nil(t, err)
nonce, proof := mine(addr, count, difficulty)
msg := NewMineMsg(addr, difficulty, count, nonce, proof)
result := handler(ctx, msg)
assert.Equal(t, result, sdk.Result{})
newDiff, err := keeper.GetLastDifficulty(ctx)
assert.Nil(t, err)
assert.Equal(t, newDiff, uint64(2))
newCount, err := keeper.GetLastCount(ctx)
assert.Nil(t, err)
assert.Equal(t, newCount, uint64(1))
// todo assert correct coin change, awaiting https://github.com/cosmos/cosmos-sdk/pull/691
difficulty = uint64(4)
nonce, proof = mine(addr, count, difficulty)
msg = NewMineMsg(addr, difficulty, count, nonce, proof)
result = handler(ctx, msg)
assert.NotEqual(t, result, sdk.Result{})
}

View File

@ -0,0 +1,113 @@
package pow
import (
"fmt"
"strconv"
sdk "github.com/cosmos/cosmos-sdk/types"
bank "github.com/cosmos/cosmos-sdk/x/bank"
)
// module users must specify coin denomination and reward (constant) per PoW solution
type PowConfig struct {
Denomination string
Reward int64
}
// genesis info must specify starting difficulty and starting count
type PowGenesis struct {
Difficulty uint64 `json:"difficulty"`
Count uint64 `json:"count"`
}
type Keeper struct {
key sdk.StoreKey
config PowConfig
ck bank.CoinKeeper
}
func NewPowConfig(denomination string, reward int64) PowConfig {
return PowConfig{denomination, reward}
}
func NewKeeper(key sdk.StoreKey, config PowConfig, ck bank.CoinKeeper) Keeper {
return Keeper{key, config, ck}
}
func (pk Keeper) InitGenesis(ctx sdk.Context, genesis PowGenesis) error {
pk.SetLastDifficulty(ctx, genesis.Difficulty)
pk.SetLastCount(ctx, genesis.Count)
return nil
}
var lastDifficultyKey = []byte("lastDifficultyKey")
func (pk Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
store := ctx.KVStore(pk.key)
stored := store.Get(lastDifficultyKey)
if stored == nil {
panic("no stored difficulty")
} else {
return strconv.ParseUint(string(stored), 0, 64)
}
}
func (pk Keeper) SetLastDifficulty(ctx sdk.Context, diff uint64) {
store := ctx.KVStore(pk.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)
stored := store.Get(countKey)
if stored == nil {
panic("no stored count")
} else {
return strconv.ParseUint(string(stored), 0, 64)
}
}
func (pk Keeper) SetLastCount(ctx sdk.Context, count uint64) {
store := ctx.KVStore(pk.key)
store.Set(countKey, []byte(strconv.FormatUint(count, 16)))
}
func (pk Keeper) CheckValid(ctx sdk.Context, difficulty uint64, count uint64) (uint64, uint64, sdk.Error) {
lastDifficulty, err := pk.GetLastDifficulty(ctx)
if err != nil {
return 0, 0, ErrNonexistentDifficulty()
}
newDifficulty := lastDifficulty + 1
lastCount, err := pk.GetLastCount(ctx)
if err != nil {
return 0, 0, ErrNonexistentCount()
}
newCount := lastCount + 1
if count != newCount {
return 0, 0, ErrInvalidCount(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 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}})
if ckErr != nil {
return ckErr
}
pk.SetLastDifficulty(ctx, newDifficulty)
pk.SetLastCount(ctx, newCount)
return nil
}

View File

@ -0,0 +1,49 @@
package pow
import (
"testing"
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/abci/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
auth "github.com/cosmos/cosmos-sdk/x/auth"
bank "github.com/cosmos/cosmos-sdk/x/bank"
)
// possibly share this kind of setup functionality between module testsuites?
func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey) {
db := dbm.NewMemDB()
capKey := sdk.NewKVStoreKey("capkey")
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db)
ms.LoadLatestVersion()
return ms, capKey
}
func TestPowKeeperGetSet(t *testing.T) {
ms, capKey := setupMultiStore()
am := auth.NewAccountMapper(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)
err := keeper.InitGenesis(ctx, PowGenesis{uint64(1), uint64(0)})
assert.Nil(t, err)
res, err := keeper.GetLastDifficulty(ctx)
assert.Nil(t, err)
assert.Equal(t, res, uint64(1))
keeper.SetLastDifficulty(ctx, 2)
res, err = keeper.GetLastDifficulty(ctx)
assert.Nil(t, err)
assert.Equal(t, res, uint64(2))
}

View File

@ -0,0 +1,44 @@
package pow
import (
"encoding/hex"
"math"
"strconv"
sdk "github.com/cosmos/cosmos-sdk/types"
crypto "github.com/tendermint/go-crypto"
)
func GenerateMineMsg(sender sdk.Address, count uint64, difficulty uint64) MineMsg {
nonce, hash := mine(sender, count, difficulty)
return NewMineMsg(sender, difficulty, count, nonce, hash)
}
func hash(sender sdk.Address, count uint64, nonce uint64) []byte {
var bytes []byte
bytes = append(bytes, []byte(sender)...)
countBytes := strconv.FormatUint(count, 16)
bytes = append(bytes, countBytes...)
nonceBytes := strconv.FormatUint(nonce, 16)
bytes = append(bytes, nonceBytes...)
hash := crypto.Sha256(bytes)
// uint64, so we just use the first 8 bytes of the hash
// this limits the range of possible difficulty values (as compared to uint256), but fine for proof-of-concept
ret := make([]byte, hex.EncodedLen(len(hash)))
hex.Encode(ret, hash)
return ret[:16]
}
func mine(sender sdk.Address, count uint64, difficulty uint64) (uint64, []byte) {
target := math.MaxUint64 / difficulty
for nonce := uint64(0); ; nonce++ {
hash := hash(sender, count, nonce)
hashuint, err := strconv.ParseUint(string(hash), 16, 64)
if err != nil {
panic(err)
}
if hashuint < target {
return nonce, hash
}
}
}

View File

@ -0,0 +1,78 @@
package pow
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"math"
"strconv"
crypto "github.com/tendermint/go-crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// MineMsg - mine some coins with PoW
type MineMsg struct {
Sender sdk.Address `json:"sender"`
Difficulty uint64 `json:"difficulty"`
Count uint64 `json:"count"`
Nonce uint64 `json:"nonce"`
Proof []byte `json:"proof"`
}
// enforce the msg type at compile time
var _ sdk.Msg = MineMsg{}
// 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}
}
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)
}
func (msg MineMsg) ValidateBasic() sdk.Error {
// check hash
var data []byte
// hash must include sender, so no other users can race the tx
data = append(data, []byte(msg.Sender)...)
countBytes := strconv.FormatUint(msg.Count, 16)
// hash must include count so proof-of-work solutions cannot be replayed
data = append(data, countBytes...)
nonceBytes := strconv.FormatUint(msg.Nonce, 16)
data = append(data, nonceBytes...)
hash := crypto.Sha256(data)
hashHex := make([]byte, hex.EncodedLen(len(hash)))
hex.Encode(hashHex, hash)
hashHex = hashHex[:16]
if !bytes.Equal(hashHex, msg.Proof) {
return ErrInvalidProof(fmt.Sprintf("hashHex: %s, proof: %s", hashHex, msg.Proof))
}
// check proof below difficulty
// difficulty is linear - 1 = all hashes, 2 = half of hashes, 3 = third of hashes, etc
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))
}
if hashUint >= target {
return ErrNotBelowTarget(fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
}
return nil
}
func (msg MineMsg) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return b
}

View File

@ -0,0 +1,78 @@
package pow
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestNewMineMsg(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 0, 0, 0, []byte("")}
equiv := NewMineMsg(addr, 0, 0, 0, []byte(""))
assert.Equal(t, msg, equiv, "%s != %s", msg, equiv)
}
func TestMineMsgType(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 0, 0, 0, []byte("")}
assert.Equal(t, msg.Type(), "pow")
}
func TestMineMsgValidation(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
nonce, proof := mine(addr, count, difficulty)
msg := MineMsg{addr, difficulty, count, nonce, proof}
err := msg.ValidateBasic()
assert.Nil(t, err, "error with difficulty %d - %+v", difficulty, err)
msg.Count += 1
err = msg.ValidateBasic()
assert.NotNil(t, err, "count was wrong, should have thrown error with msg %s", msg)
msg.Count -= 1
msg.Nonce += 1
err = msg.ValidateBasic()
assert.NotNil(t, err, "nonce was wrong, should have thrown error with msg %s", msg)
msg.Nonce -= 1
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) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 0, 0, 0, []byte("abc")}
res := msg.String()
assert.Equal(t, res, "MineMsg{Sender: 73656E646572, Difficulty: 0, Count: 0, Nonce: 0, Proof: abc}")
}
func TestMineMsgGet(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 0, 0, 0, []byte("")}
res := msg.Get(nil)
assert.Nil(t, res)
}
func TestMineMsgGetSignBytes(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 1, 1, 1, []byte("abc")}
res := msg.GetSignBytes()
assert.Equal(t, string(res), `{"sender":"73656E646572","difficulty":1,"count":1,"nonce":1,"proof":"YWJj"}`)
}
func TestMineMsgGetSigners(t *testing.T) {
addr := sdk.Address([]byte("sender"))
msg := MineMsg{addr, 1, 1, 1, []byte("abc")}
res := msg.GetSigners()
assert.Equal(t, fmt.Sprintf("%v", res), "[73656E646572]")
}

View File

@ -1,3 +0,0 @@
Gaiad is the abci application, which can be run stand-alone, or in-process with tendermint.
Gaiacli is a client application, which connects to tendermint rpc, and sends transactions and queries the state. It uses light-client proofs to guarantee the results even if it doesn't have 100% trust in the node it connects to.

View File

@ -1,71 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"os"
"github.com/spf13/cobra"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/cli"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/version"
)
// gaiadCmd is the entry point for this binary
var (
gaiadCmd = &cobra.Command{
Use: "gaiad",
Short: "Gaia Daemon (server)",
}
)
// defaultOptions sets up the app_options for the
// default genesis file
func defaultOptions(args []string) (json.RawMessage, error) {
addr, secret, err := server.GenerateCoinKey()
if err != nil {
return nil, err
}
fmt.Println("Secret phrase to access coins:")
fmt.Println(secret)
opts := fmt.Sprintf(`{
"accounts": [{
"address": "%s",
"coins": [
{
"denom": "mycoin",
"amount": 9007199254740992
}
]
}]
}`, addr)
return json.RawMessage(opts), nil
}
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
// TODO: set this to something real
app := new(baseapp.BaseApp)
return app, nil
}
func main() {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
With("module", "main")
gaiadCmd.AddCommand(
server.InitCmd(defaultOptions, logger),
server.StartCmd(generateApp, logger),
server.UnsafeResetAllCmd(logger),
version.VersionCmd,
)
// prepare and add flags
executor := cli.PrepareBaseCmd(gaiadCmd, "GA", os.ExpandEnv("$HOME/.gaiad"))
executor.Execute()
}

View File

@ -3,8 +3,12 @@ package main
import (
"fmt"
"os"
"path/filepath"
"github.com/spf13/viper"
"github.com/tendermint/abci/server"
"github.com/tendermint/tmlibs/cli"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@ -17,7 +21,8 @@ func main() {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main")
db, err := dbm.NewGoLevelDB("basecoind", "data")
rootDir := viper.GetString(cli.HomeFlag)
db, err := dbm.NewGoLevelDB("basecoind", filepath.Join(rootDir, "data"))
if err != nil {
fmt.Println(err)
os.Exit(1)
@ -30,13 +35,13 @@ func main() {
var baseApp = bam.NewBaseApp("kvstore", logger, db)
// Set mounts for BaseApp's MultiStore.
baseApp.MountStore(capKeyMainStore, sdk.StoreTypeIAVL)
baseApp.MountStoresIAVL(capKeyMainStore)
// Set Tx decoder
baseApp.SetTxDecoder(decodeTx)
// Set a handler Route.
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore), nil)
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore))
// Load latest version.
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {

View File

@ -38,7 +38,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) {
baseApp.SetInitChainer(InitChainer(capKeyMainStore))
// Set a handler Route.
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore), nil)
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore))
// Load latest version.
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
@ -106,7 +106,7 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci
// GenInitOptions can be passed into InitCmd,
// returns a static string of a few key-values that can be parsed
// by InitChainer
func GenInitOptions(args []string) (json.RawMessage, error) {
func GenInitOptions(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
opts := []byte(`{
"values": [
{

View File

@ -21,7 +21,7 @@ func TestInitApp(t *testing.T) {
require.NoError(t, err)
// initialize it future-way
opts, err := GenInitOptions(nil)
opts, err := GenInitOptions(nil, nil, "")
require.NoError(t, err)
req := abci.RequestInitChain{AppStateBytes: opts}
app.InitChain(req)

View File

@ -1,62 +0,0 @@
// +build scripts
package main
/*
import (
"encoding/json"
"fmt"
"os"
"time"
"github.com/gorilla/websocket"
"github.com/tendermint/go-wire"
_ "github.com/tendermint/tendermint/rpc/core/types" // Register RPCResponse > Result types
"github.com/tendermint/tendermint/rpc/lib/client"
"github.com/tendermint/tendermint/rpc/lib/types"
cmn "github.com/tendermint/tmlibs/common"
)
func main() {
ws := rpcclient.NewWSClient(os.Args[1]+":46657", "/websocket")
_, err := ws.Start()
if err != nil {
cmn.Exit(err.Error())
}
// Read a bunch of responses
go func() {
for {
res, ok := <-ws.ResultsCh
if !ok {
break
}
//fmt.Println(counter, "res:", Blue(string(res)))
var result []interface{}
err := json.Unmarshal([]byte(string(res)), &result)
if err != nil {
Exit("Error unmarshalling block: " + err.Error())
}
height := result[1].(map[string]interface{})["block"].(map[string]interface{})["header"].(map[string]interface{})["height"]
txs := result[1].(map[string]interface{})["block"].(map[string]interface{})["data"].(map[string]interface{})["txs"]
if len(txs.([]interface{})) > 0 {
fmt.Println(">>", height, txs)
}
}
}()
for i := 0; i < 100000; i++ {
request := rpctypes.NewRPCRequest("fakeid", "block", Arr(i))
reqBytes := wire.JSONBytes(request)
err = ws.WriteMessage(websocket.TextMessage, reqBytes)
if err != nil {
cmn.Exit("writing websocket request: " + err.Error())
}
}
time.Sleep(time.Second * 1000)
ws.Stop()
}
*/

View File

@ -2,53 +2,63 @@ package server
import (
"encoding/json"
"fmt"
"io/ioutil"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/go-crypto/keys"
"github.com/tendermint/go-crypto/keys/words"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
)
// testnetInformation contains the info necessary
// to setup a testnet including this account and validator.
type testnetInformation struct {
Secret string `json:"secret"`
ChainID string `json:"chain_id"`
Account string `json:"account"`
Validator tmtypes.GenesisValidator `json:"validator"`
NodeID p2p.ID `json:"node_id"`
}
type initCmd struct {
genAppState GenAppState
context *Context
}
// InitCmd will initialize all files for tendermint,
// along with proper app_state.
// The application can pass in a function to generate
// proper state. And may want to use GenerateCoinKey
// to create default account(s).
func InitCmd(gen GenAppState, logger log.Logger) *cobra.Command {
func InitCmd(gen GenAppState, ctx *Context) *cobra.Command {
cmd := initCmd{
genAppState: gen,
logger: logger,
context: ctx,
}
return &cobra.Command{
cobraCmd := cobra.Command{
Use: "init",
Short: "Initialize genesis files",
RunE: cmd.run,
}
}
// GenAppState can parse command-line and flag to
// generate default app_state for the genesis file.
// This is application-specific
type GenAppState func(args []string) (json.RawMessage, error)
type initCmd struct {
genAppState GenAppState
logger log.Logger
return &cobraCmd
}
func (c initCmd) run(cmd *cobra.Command, args []string) error {
// Store testnet information as we go
var testnetInfo testnetInformation
// Run the basic tendermint initialization,
// set up a default genesis with no app_options
config, err := tcmd.ParseConfig()
if err != nil {
return err
}
err = c.initTendermintFiles(config)
config := c.context.Config
err := c.initTendermintFiles(config, &testnetInfo)
if err != nil {
return err
}
@ -58,36 +68,61 @@ func (c initCmd) run(cmd *cobra.Command, args []string) error {
return nil
}
// Now, we want to add the custom app_state
appState, err := c.genAppState(args)
// generate secrete and address
addr, secret, err := GenerateCoinKey()
if err != nil {
return err
}
var DEFAULT_DENOM = "mycoin"
// Now, we want to add the custom app_state
appState, err := c.genAppState(args, addr, DEFAULT_DENOM)
if err != nil {
return err
}
testnetInfo.Secret = secret
testnetInfo.Account = addr.String()
// And add them to the genesis file
genFile := config.GenesisFile()
return addGenesisState(genFile, appState)
if err := addGenesisState(genFile, appState); err != nil {
return err
}
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return err
}
testnetInfo.NodeID = nodeKey.ID()
out, err := json.MarshalIndent(testnetInfo, "", " ")
if err != nil {
return err
}
fmt.Println(string(out))
return nil
}
// This was copied from tendermint/cmd/tendermint/commands/init.go
// so we could pass in the config and the logger.
func (c initCmd) initTendermintFiles(config *cfg.Config) error {
func (c initCmd) initTendermintFiles(config *cfg.Config, info *testnetInformation) error {
// private validator
privValFile := config.PrivValidatorFile()
var privValidator *tmtypes.PrivValidatorFS
if cmn.FileExists(privValFile) {
privValidator = tmtypes.LoadPrivValidatorFS(privValFile)
c.logger.Info("Found private validator", "path", privValFile)
c.context.Logger.Info("Found private validator", "path", privValFile)
} else {
privValidator = tmtypes.GenPrivValidatorFS(privValFile)
privValidator.Save()
c.logger.Info("Generated private validator", "path", privValFile)
c.context.Logger.Info("Generated private validator", "path", privValFile)
}
// genesis file
genFile := config.GenesisFile()
if cmn.FileExists(genFile) {
c.logger.Info("Found genesis file", "path", genFile)
c.context.Logger.Info("Found genesis file", "path", genFile)
} else {
genDoc := tmtypes.GenesisDoc{
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
@ -100,11 +135,52 @@ func (c initCmd) initTendermintFiles(config *cfg.Config) error {
if err := genDoc.SaveAs(genFile); err != nil {
return err
}
c.logger.Info("Generated genesis file", "path", genFile)
c.context.Logger.Info("Generated genesis file", "path", genFile)
}
// reload the config file and find our validator info
loadedDoc, err := tmtypes.GenesisDocFromFile(genFile)
if err != nil {
return err
}
for _, validator := range loadedDoc.Validators {
if validator.PubKey == privValidator.GetPubKey() {
info.Validator = validator
}
}
info.ChainID = loadedDoc.ChainID
return nil
}
//-------------------------------------------------------------------
// GenAppState takes the command line args, as well
// as an address and coin denomination.
// It returns a default app_state to be included in
// in the genesis file.
// This is application-specific
type GenAppState func(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error)
// 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) {
opts := fmt.Sprintf(`{
"accounts": [{
"address": "%s",
"coins": [
{
"denom": "%s",
"amount": 9007199254740992
}
]
}]
}`, addr.String(), coinDenom)
return json.RawMessage(opts), nil
}
//-------------------------------------------------------------------
// GenesisDoc involves some tendermint-specific structures we don't
// want to parse, so we just grab it into a raw object format,
// so we can add one line.
@ -130,3 +206,30 @@ func addGenesisState(filename string, appState json.RawMessage) error {
return ioutil.WriteFile(filename, out, 0600)
}
//-------------------------------------------------------------------
// GenerateCoinKey returns the address of a public key,
// along with the secret phrase to recover the private key.
// You can give coins to this address and return the recovery
// phrase to the user to access them.
func GenerateCoinKey() (sdk.Address, string, error) {
// construct an in-memory key store
codec, err := words.LoadCodec("english")
if err != nil {
return nil, "", err
}
keybase := keys.New(
dbm.NewMemDB(),
codec,
)
// generate a private key, with recovery phrase
info, secret, err := keybase.Create("name", "pass", keys.AlgoEd25519)
if err != nil {
return nil, "", err
}
addr := info.PubKey.Address()
return addr, secret, nil
}

View File

@ -8,13 +8,17 @@ import (
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/mock"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
)
func TestInit(t *testing.T) {
defer setupViper(t)()
logger := log.NewNopLogger()
cmd := InitCmd(mock.GenInitOptions, logger)
err := cmd.RunE(nil, nil)
cfg, err := tcmd.ParseConfig()
require.Nil(t, err)
ctx := NewContext(cfg, logger)
cmd := InitCmd(mock.GenInitOptions, ctx)
err = cmd.RunE(nil, nil)
require.NoError(t, err)
}

View File

@ -1,34 +0,0 @@
package server
import (
"github.com/tendermint/go-crypto/keys"
"github.com/tendermint/go-crypto/keys/words"
dbm "github.com/tendermint/tmlibs/db"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// GenerateCoinKey returns the address of a public key,
// along with the secret phrase to recover the private key.
// You can give coins to this address and return the recovery
// phrase to the user to access them.
func GenerateCoinKey() (sdk.Address, string, error) {
// construct an in-memory key store
codec, err := words.LoadCodec("english")
if err != nil {
return nil, "", err
}
keybase := keys.New(
dbm.NewMemDB(),
codec,
)
// generate a private key, with recovery phrase
info, secret, err := keybase.Create("name", "pass", keys.AlgoEd25519)
if err != nil {
return nil, "", err
}
addr := info.PubKey.Address()
return addr, secret, nil
}

View File

@ -1,31 +0,0 @@
package server
import (
"github.com/spf13/cobra"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tmlibs/log"
)
// UnsafeResetAllCmd - extension of the tendermint command, resets initialization
func UnsafeResetAllCmd(logger log.Logger) *cobra.Command {
cmd := resetAll{logger}
return &cobra.Command{
Use: "unsafe_reset_all",
Short: "Reset all blockchain data",
RunE: cmd.run,
}
}
type resetAll struct {
logger log.Logger
}
func (r resetAll) run(cmd *cobra.Command, args []string) error {
cfg, err := tcmd.ParseConfig()
if err != nil {
return err
}
tcmd.ResetAll(cfg.DBDir(), cfg.PrivValidatorFile(), r.logger)
return nil
}

View File

@ -1,38 +0,0 @@
package server
import (
"fmt"
"github.com/spf13/cobra"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tmlibs/log"
)
// ShowNodeIdCmd - ported from Tendermint, dump node ID to stdout
func ShowNodeIdCmd(logger log.Logger) *cobra.Command {
cmd := showNodeId{logger}
return &cobra.Command{
Use: "show_node_id",
Short: "Show this node's ID",
RunE: cmd.run,
}
}
type showNodeId struct {
logger log.Logger
}
func (s showNodeId) run(cmd *cobra.Command, args []string) error {
cfg, err := tcmd.ParseConfig()
if err != nil {
return err
}
nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile())
if err != nil {
return err
}
fmt.Println(nodeKey.ID())
return nil
}

View File

@ -1,40 +0,0 @@
package server
import (
"fmt"
"github.com/spf13/cobra"
"github.com/tendermint/go-wire/data"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/log"
)
// ShowValidator - ported from Tendermint, show this node's validator info
func ShowValidatorCmd(logger log.Logger) *cobra.Command {
cmd := showValidator{logger}
return &cobra.Command{
Use: "show_validator",
Short: "Show this node's validator info",
RunE: cmd.run,
}
}
type showValidator struct {
logger log.Logger
}
func (s showValidator) run(cmd *cobra.Command, args []string) error {
cfg, err := tcmd.ParseConfig()
if err != nil {
return err
}
privValidator := types.LoadOrGenPrivValidatorFS(cfg.PrivValidatorFile())
pubKeyJSONBytes, err := data.ToJSON(privValidator.PubKey)
if err != nil {
return err
}
fmt.Println(string(pubKeyJSONBytes))
return nil
}

View File

@ -21,16 +21,16 @@ const (
flagAddress = "address"
)
// appGenerator lets us lazily initialize app, using home dir
// AppCreator lets us lazily initialize app, using home dir
// and other flags (?) to start
type appCreator func(string, log.Logger) (abci.Application, error)
type AppCreator func(string, log.Logger) (abci.Application, error)
// StartCmd runs the service passed in, either
// stand-alone, or in-process with tendermint
func StartCmd(app appCreator, logger log.Logger) *cobra.Command {
func StartCmd(app AppCreator, ctx *Context) *cobra.Command {
start := startCmd{
appCreator: app,
logger: logger,
context: ctx,
}
cmd := &cobra.Command{
Use: "start",
@ -48,16 +48,16 @@ func StartCmd(app appCreator, logger log.Logger) *cobra.Command {
}
type startCmd struct {
appCreator appCreator
logger log.Logger
appCreator AppCreator
context *Context
}
func (s startCmd) run(cmd *cobra.Command, args []string) error {
if !viper.GetBool(flagWithTendermint) {
s.logger.Info("Starting ABCI without Tendermint")
s.context.Logger.Info("Starting ABCI without Tendermint")
return s.startStandAlone()
}
s.logger.Info("Starting ABCI with Tendermint")
s.context.Logger.Info("Starting ABCI with Tendermint")
return s.startInProcess()
}
@ -65,7 +65,7 @@ func (s startCmd) startStandAlone() error {
// Generate the app in the proper dir
addr := viper.GetString(flagAddress)
home := viper.GetString("home")
app, err := s.appCreator(home, s.logger)
app, err := s.appCreator(home, s.context.Logger)
if err != nil {
return err
}
@ -74,7 +74,7 @@ func (s startCmd) startStandAlone() error {
if err != nil {
return errors.Errorf("Error creating listener: %v\n", err)
}
svr.SetLogger(s.logger.With("module", "abci-server"))
svr.SetLogger(s.context.Logger.With("module", "abci-server"))
svr.Start()
// Wait forever
@ -86,13 +86,9 @@ func (s startCmd) startStandAlone() error {
}
func (s startCmd) startInProcess() error {
cfg, err := tcmd.ParseConfig()
if err != nil {
return err
}
cfg := s.context.Config
home := cfg.RootDir
app, err := s.appCreator(home, s.logger)
app, err := s.appCreator(home, s.context.Logger)
if err != nil {
return err
}
@ -103,7 +99,7 @@ func (s startCmd) startInProcess() error {
proxy.NewLocalClientCreator(app),
node.DefaultGenesisDocProviderFunc(cfg),
node.DefaultDBProvider,
s.logger.With("module", "node"))
s.context.Logger.With("module", "node"))
if err != nil {
return err
}

View File

@ -1,7 +1,8 @@
package server
import (
// "os"
"io/ioutil"
"os"
"testing"
"time"
@ -9,47 +10,56 @@ import (
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/mock"
"github.com/tendermint/abci/server"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tmlibs/log"
)
func TestStartStandAlone(t *testing.T) {
defer setupViper(t)()
home, err := ioutil.TempDir("", "mock-sdk-cmd")
defer func() {
os.RemoveAll(home)
}()
logger := log.NewNopLogger()
initCmd := InitCmd(mock.GenInitOptions, logger)
err := initCmd.RunE(nil, nil)
cfg, err := tcmd.ParseConfig()
require.Nil(t, err)
ctx := NewContext(cfg, logger)
initCmd := InitCmd(mock.GenInitOptions, ctx)
err = initCmd.RunE(nil, nil)
require.NoError(t, err)
// set up app and start up
viper.Set(flagWithTendermint, false)
viper.Set(flagAddress, "localhost:11122")
startCmd := StartCmd(mock.NewApp, logger)
startCmd.Flags().Set(flagAddress, FreeTCPAddr(t)) // set to a new free address
timeout := time.Duration(3) * time.Second
app, err := mock.NewApp(home, logger)
require.Nil(t, err)
svr, err := server.NewServer(FreeTCPAddr(t), "socket", app)
require.Nil(t, err, "Error creating listener")
svr.SetLogger(logger.With("module", "abci-server"))
svr.Start()
RunOrTimeout(startCmd, timeout, t)
timer := time.NewTimer(time.Duration(5) * time.Second)
select {
case <-timer.C:
svr.Stop()
}
}
/*
func TestStartWithTendermint(t *testing.T) {
defer setupViper(t)()
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
With("module", "mock-cmd")
// logger := log.NewNopLogger()
initCmd := InitCmd(mock.GenInitOptions, logger)
err := initCmd.RunE(nil, nil)
cfg, err := tcmd.ParseConfig()
require.Nil(t, err)
ctx := NewContext(cfg, logger)
initCmd := InitCmd(mock.GenInitOptions, ctx)
err = initCmd.RunE(nil, nil)
require.NoError(t, err)
// set up app and start up
viper.Set(flagWithTendermint, true)
startCmd := StartCmd(mock.NewApp, logger)
startCmd := StartCmd(mock.NewApp, ctx)
startCmd.Flags().Set(flagAddress, FreeTCPAddr(t)) // set to a new free address
timeout := time.Duration(3) * time.Second
timeout := time.Duration(5) * time.Second
//a, _ := startCmd.Flags().GetString(flagAddress)
//panic(a)
RunOrTimeout(startCmd, timeout, t)
close(RunOrTimeout(startCmd, timeout, t))
}
*/

View File

@ -12,6 +12,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tmlibs/cli"
"github.com/tendermint/tmlibs/log"
)
@ -44,14 +45,18 @@ func setupViper(t *testing.T) func() {
func StartServer(t *testing.T) chan error {
defer setupViper(t)()
cfg, err := tcmd.ParseConfig()
require.Nil(t, err)
// init server
initCmd := InitCmd(mock.GenInitOptions, log.NewNopLogger())
err := initCmd.RunE(nil, nil)
ctx := NewContext(cfg, log.NewNopLogger())
initCmd := InitCmd(mock.GenInitOptions, ctx)
err = initCmd.RunE(nil, nil)
require.NoError(t, err)
// start server
viper.Set(flagWithTendermint, true)
startCmd := StartCmd(mock.NewApp, log.NewNopLogger())
startCmd := StartCmd(mock.NewApp, ctx)
startCmd.Flags().Set(flagAddress, FreeTCPAddr(t)) // set to a new free address
startCmd.Flags().Set("rpc.laddr", FreeTCPAddr(t)) // set to a new free address
timeout := time.Duration(3) * time.Second

85
server/tm_cmds.go Normal file
View File

@ -0,0 +1,85 @@
package server
import (
"fmt"
"github.com/spf13/cobra"
"github.com/tendermint/go-wire/data"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
)
// 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,
}
}
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{
Use: "show_validator",
Short: "Show this node's validator info",
RunE: cmd.run,
}
}
type showValidator struct {
context *Context
}
func (s showValidator) run(cmd *cobra.Command, args []string) error {
cfg := s.context.Config
privValidator := types.LoadOrGenPrivValidatorFS(cfg.PrivValidatorFile())
pubKeyJSONBytes, err := data.ToJSON(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,
}
}
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
}

77
server/util.go Normal file
View File

@ -0,0 +1,77 @@
package server
import (
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/version"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tmlibs/cli"
tmflags "github.com/tendermint/tmlibs/cli/flags"
"github.com/tendermint/tmlibs/log"
)
type Context struct {
Config *cfg.Config
Logger log.Logger
}
func NewDefaultContext() *Context {
return NewContext(
cfg.DefaultConfig(),
log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
)
}
func NewContext(config *cfg.Config, logger log.Logger) *Context {
return &Context{config, logger}
}
//--------------------------------------------------------------------
// PersistentPreRunEFn returns a PersistentPreRunE function for cobra
// that initailizes the passed in context with a properly configured
// logger and config objecy
func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error {
if cmd.Name() == version.VersionCmd.Name() {
return nil
}
config, err := tcmd.ParseConfig()
if err != nil {
return err
}
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel())
if err != nil {
return err
}
if viper.GetBool(cli.TraceFlag) {
logger = log.NewTracingLogger(logger)
}
logger = logger.With("module", "main")
context.Config = config
context.Logger = logger
return nil
}
}
func AddCommands(
rootCmd *cobra.Command,
appState GenAppState, appCreator AppCreator,
context *Context) {
rootCmd.PersistentFlags().String("log_level", context.Config.LogLevel, "Log level")
rootCmd.AddCommand(
InitCmd(appState, context),
StartCmd(appCreator, context),
UnsafeResetAllCmd(context),
ShowNodeIDCmd(context),
ShowValidatorCmd(context),
version.VersionCmd,
)
}

View File

@ -144,7 +144,7 @@ func (st *iavlStore) ReverseSubspaceIterator(prefix []byte) Iterator {
func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
if len(req.Data) == 0 {
msg := "Query cannot be zero length"
return sdk.ErrTxDecode(msg).Result().ToQuery()
return sdk.ErrTxDecode(msg).QueryResult()
}
tree := st.tree
@ -178,7 +178,7 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
default:
msg := fmt.Sprintf("Unexpected Query path: %v", req.Path)
return sdk.ErrUnknownRequest(msg).Result().ToQuery()
return sdk.ErrUnknownRequest(msg).QueryResult()
}
return
}

View File

@ -205,18 +205,18 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery {
path := req.Path
storeName, subpath, err := parsePath(path)
if err != nil {
return err.Result().ToQuery()
return err.QueryResult()
}
store := rs.getStoreByName(storeName)
if store == nil {
msg := fmt.Sprintf("no such store: %s", storeName)
return sdk.ErrUnknownRequest(msg).Result().ToQuery()
return sdk.ErrUnknownRequest(msg).QueryResult()
}
queryable, ok := store.(Queryable)
if !ok {
msg := fmt.Sprintf("store %s doesn't support queries", storeName)
return sdk.ErrUnknownRequest(msg).Result().ToQuery()
return sdk.ErrUnknownRequest(msg).QueryResult()
}
// trim the path and make the query

View File

@ -257,7 +257,10 @@ func (coins Coins) Swap(i, j int) { coins[i], coins[j] = coins[j], coins[i]
var _ sort.Interface = Coins{}
// Sort is a helper function to sort the set of coins inplace
func (coins Coins) Sort() { sort.Sort(coins) }
func (coins Coins) Sort() Coins {
sort.Sort(coins)
return coins
}
//----------------------------------------
// Parsing

View File

@ -3,6 +3,8 @@ package types
import (
"fmt"
"runtime"
abci "github.com/tendermint/abci/types"
)
// ABCI Response Code
@ -121,6 +123,7 @@ type Error interface {
TraceCause(cause error, msg string) Error
Cause() error
Result() Result
QueryResult() abci.ResponseQuery
}
func NewError(code CodeType, msg string) Error {
@ -220,3 +223,11 @@ func (err *sdkError) Result() Result {
Log: err.ABCILog(),
}
}
// QueryResult allows us to return sdk.Error.QueryResult() in query responses
func (err *sdkError) QueryResult() abci.ResponseQuery {
return abci.ResponseQuery{
Code: uint32(err.ABCICode()),
Log: err.ABCILog(),
}
}

View File

@ -38,11 +38,3 @@ type Result struct {
func (res Result) IsOK() bool {
return res.Code.IsOK()
}
// ToQuery allows us to return sdk.Error.Result() in query responses
func (res Result) ToQuery() abci.ResponseQuery {
return abci.ResponseQuery{
Code: uint32(res.Code),
Log: res.Log,
}
}

View File

@ -6,10 +6,10 @@ package version
// TODO improve
const Maj = "0"
const Min = "12"
const Fix = "0"
const Min = "13"
const Fix = "2"
const Version = "0.12.0"
const Version = "0.13.2-dev"
// GitCommit set by build flags
var GitCommit = ""

View File

@ -8,7 +8,7 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
@ -64,7 +64,9 @@ func (c commander) getAccountCmd(cmd *cobra.Command, args []string) error {
}
key := sdk.Address(bz)
res, err := builder.Query(key, c.storeName)
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(key, c.storeName)
if err != nil {
return err
}

View File

@ -8,7 +8,7 @@ import (
"github.com/gorilla/mux"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
@ -21,6 +21,7 @@ type commander struct {
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)
addr := vars["address"]
@ -33,7 +34,7 @@ func QueryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder sdk.A
}
key := sdk.Address(bz)
res, err := builder.Query(key, c.storeName)
res, err := ctx.Query(key, c.storeName)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error())))

View File

@ -8,7 +8,7 @@ import (
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/bank"
@ -38,8 +38,10 @@ type Commander struct {
}
func (c Commander) sendTxCmd(cmd *cobra.Command, args []string) error {
ctx := context.NewCoreContextFromViper()
// get the from address
from, err := builder.GetFromAddress()
from, err := ctx.GetFromAddress()
if err != nil {
return err
}
@ -59,14 +61,11 @@ func (c Commander) sendTxCmd(cmd *cobra.Command, args []string) error {
}
to := sdk.Address(bz)
// get account name
name := viper.GetString(client.FlagName)
// build message
msg := BuildMsg(from, to, coins)
// build and sign the transaction, then broadcast to Tendermint
res, err := builder.SignBuildBroadcast(name, msg, c.Cdc)
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, c.Cdc)
if err != nil {
return err
}

View File

@ -195,7 +195,7 @@ func (out Output) ValidateBasic() sdk.Error {
}
func (out Output) String() string {
return fmt.Sprintf("Output{%X,%v}", out.Address, out.Coins)
return fmt.Sprintf("Output{%v,%v}", out.Address, out.Coins)
}
// NewOutput - create a transaction output, used with SendMsg

View File

@ -179,8 +179,10 @@ func TestSendMsgValidation(t *testing.T) {
func TestSendMsgString(t *testing.T) {
// Construct a SendMsg
addr1 := sdk.Address([]byte("input"))
addr2 := sdk.Address([]byte("output"))
addr1String := "input"
addr2String := "output"
addr1 := sdk.Address([]byte(addr1String))
addr2 := sdk.Address([]byte(addr2String))
coins := sdk.Coins{{"atom", 10}}
var msg = SendMsg{
Inputs: []Input{NewInput(addr1, coins)},
@ -188,8 +190,9 @@ func TestSendMsgString(t *testing.T) {
}
res := msg.String()
expected := fmt.Sprintf("SendMsg{[Input{%X,10atom}]->[Output{%X,10atom}]}", addr1String, addr2String)
// TODO some failures for bad results
assert.Equal(t, res, "SendMsg{[Input{696E707574,10atom}]->[Output{364637353734373037353734,10atom}]}")
assert.Equal(t, expected, res)
}
func TestSendMsgGet(t *testing.T) {
@ -275,16 +278,18 @@ func TestIssueMsgValidation(t *testing.T) {
}
func TestIssueMsgString(t *testing.T) {
addrString := "loan-from-bank"
bankerString := "input"
// Construct a IssueMsg
addr := sdk.Address([]byte("loan-from-bank"))
addr := sdk.Address([]byte(addrString))
coins := sdk.Coins{{"atom", 10}}
var msg = IssueMsg{
Banker: sdk.Address([]byte("input")),
Banker: sdk.Address([]byte(bankerString)),
Outputs: []Output{NewOutput(addr, coins)},
}
res := msg.String()
// TODO: FIX THIS OUTPUT!
assert.Equal(t, res, "IssueMsg{696E707574#[Output{36433646363136453244363637323646364432443632363136453642,10atom}]}")
expected := fmt.Sprintf("IssueMsg{%X#[Output{%X,10atom}]}", bankerString, addrString)
assert.Equal(t, expected, res)
}
func TestIssueMsgGet(t *testing.T) {

View File

@ -7,11 +7,9 @@ import (
"net/http"
"github.com/gorilla/mux"
"github.com/spf13/viper"
"github.com/tendermint/go-crypto/keys"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/bank/commands"
@ -30,6 +28,7 @@ type sendBody struct {
// SendRequestHandler - http request handler to send coins to a address
func SendRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWriter, *http.Request) {
c := commands.Commander{cdc}
ctx := context.NewCoreContextFromViper()
return func(w http.ResponseWriter, r *http.Request) {
// collect data
vars := mux.Vars(r)
@ -73,9 +72,8 @@ func SendRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWrit
}
// sign
// XXX: OMG
viper.Set(client.FlagSequence, m.Sequence)
txBytes, err := builder.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
ctx = ctx.WithSequence(m.Sequence)
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(err.Error()))
@ -83,7 +81,7 @@ func SendRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWrit
}
// send
res, err := builder.BroadcastTx(txBytes)
res, err := ctx.BroadcastTx(txBytes)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))

View File

@ -1,25 +1,157 @@
# IBC CLI Usage
# IBC Doubble Hubble
## initialize
## Remove remaining data
```bash
basecoind init # copy the recover key
basecli keys add keyname --recover
basecoind start
```console
> rm -r ~/.chain1
> rm -r ~/.chain2
> rm -r ~/.basecli
```
## transfer
## Initialize both chains
`transfer` sends coins from one chain to another(or itself).
```console
> basecoind init --home ~/.chain1
I[04-02|14:03:33.704] Generated private validator module=main path=/home/mossid/.chain1/config/priv_validator.json
I[04-02|14:03:33.705] Generated genesis file module=main path=/home/mossid/.chain1/config/genesis.json
{
"secret": "crunch ignore trigger neither differ dance cheap brick situate floor luxury citizen husband decline arrow abandon",
"account": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC",
"validator": {
"pub_key": {
"type": "ed25519",
"data": "8C9917D5E982E221F5A1450103102B44BBFC1E8768126C606246CB37B5794F4D"
},
"power": 10,
"name": ""
},
"node_id": "3ac8e6242315fd62143dc3e52c161edaaa6b1a64",
"chain_id": "test-chain-ZajMfr"
}
> ADDR1=C69FEB398A29AAB1B3C4F07DE22208F35E711BCC
> ID1=test-chain-ZajMfr
> NODE1=tcp://0.0.0.0:36657
> basecli keys add key1 --recover
Enter a passphrase for your key:
Repeat the passphrase:
Enter your recovery seed phrase:
crunch ignore trigger neither differ dance cheap brick situate floor luxury citizen husband decline arrow abandon
key1 C69FEB398A29AAB1B3C4F07DE22208F35E711BCC
> basecoind init --home ~/.chain2
I[04-02|14:09:14.453] Generated private validator module=main path=/home/mossid/.chain2/config/priv_validator.json
I[04-02|14:09:14.453] Generated genesis file module=main path=/home/mossid/.chain2/config/genesis.json
{
"secret": "age guide awesome month female left oxygen soccer define high grocery work desert dinner arena abandon",
"account": "DC26002735D3AA9573707CFA6D77C12349E49868",
"validator": {
"pub_key": {
"type": "ed25519",
"data": "A94FE4B9AD763D301F4DD5A2766009812495FB7A79F1275FB8A5AF09B44FD5F3"
},
"power": 10,
"name": ""
},
"node_id": "ad26831330e1c72b85276d53c20f0680e6fd4cf5"
"chain_id": "test-chain-4XHTPn"
}
> ADDR2=DC26002735D3AA9573707CFA6D77C12349E49868
> ID2=test-chain-4XHTPn
> NODE2=tcp://0.0.0.0:46657
> basecli keys add key2 --recover
Enter a passphrase for your key:
Repeat the passphrase:
Enter your recovery seed phrase:
age guide awesome month female left oxygen soccer define high grocery work desert dinner arena abandon
key2 DC26002735D3AA9573707CFA6D77C12349E49868
> basecoind start --home ~/.chain1 --address tcp://0.0.0.0:36658 --rpc.laddr tcp://0.0.0.0:36657 --p2p.laddr tcp://0.0.0.0:36656
...
> basecoind start --home ~/.chain2 # --address tcp://0.0.0.0:46658 --rpc.laddr tcp://0.0.0.0:46657 --p2p.laddr tcp://0.0.0.0:46656
...
```
## Check balance
```console
> basecli account $ADDR1 --node $NODE1
{
"address": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC",
"coins": [
{
"denom": "mycoin",
"amount": 9007199254740992
}
],
"public_key": null,
"sequence": 0,
"name": ""
}
> basecli account $ADDR2 --node $NODE2
{
"address": "DC26002735D3AA9573707CFA6D77C12349E49868",
"coins": [
{
"denom": "mycoin",
"amount": 9007199254740992
}
],
"public_key": null,
"sequence": 0,
"name": ""
}
```bash
basecli transfer --name keyname --to address_of_destination --amount 10mycoin --chain test-chain-AAAAAA --chain-id AAAAAA
```
The id of the chain can be found in `$HOME/.basecoind/config/genesis.json`
## Transfer coins (addr1:chain1 -> addr2:chain2)
```console
> basecli transfer --name key1 --to $ADDR2 --amount 10mycoin --chain $ID2 --chain-id $ID1 --node $NODE1
Password to sign with 'key1':
Committed at block 1022. Hash: E16019DCC4AA08CA70AFCFBC96028ABCC51B6AD0
> basecli account $ADDR1 --node $NODE1
{
"address": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC",
"coins": [
{
"denom": "mycoin",
"amount": 9007199254740982
}
],
"public_key": {
"type": "ed25519",
"data": "9828FF1780A066A0D93D840737566B697035448D6C880807322BED8919348B2B"
},
"sequence": 1,
"name": ""
}
```
## Relay IBC packets
```console
> basecli relay --name key2 --from-chain-id $ID1 --from-chain-node $NODE1 --to-chain-id $ID2 --to-chain-node $NODE2 --chain-id $ID2
Password to sign with 'key2':
I[04-03|16:18:59.984] Detected IBC packet number=0
I[04-03|16:19:00.869] Relayed IBC packet number=0
> basecli account $ADDR2 --node $NODE2
{
"address": "DC26002735D3AA9573707CFA6D77C12349E49868",
"coins": [
{
"denom": "mycoin",
"amount": 9007199254741002
}
],
"public_key": {
"type": "ed25519",
"data": "F52B4FA545F4E9BFE5D7AF1DD2236899FDEF905F9B3057C38D7C01BF1B8EB52E"
},
"sequence": 1,
"name": ""
}
## relay
```bash
basecli relay --name keyname --from-chain-id test-chain-AAAAAA --from-chain-node=tcp://0.0.0.0:46657 --to-chain-id test-chain-AAAAAA --to-chain-node=tcp://0.0.0.0:46657
```

View File

@ -8,7 +8,7 @@ import (
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
wire "github.com/cosmos/cosmos-sdk/wire"
@ -39,8 +39,10 @@ type sendCommander struct {
}
func (c sendCommander) sendIBCTransfer(cmd *cobra.Command, args []string) error {
ctx := context.NewCoreContextFromViper()
// get the from address
from, err := builder.GetFromAddress()
from, err := ctx.GetFromAddress()
if err != nil {
return err
}
@ -52,9 +54,7 @@ func (c sendCommander) sendIBCTransfer(cmd *cobra.Command, args []string) error
}
// get password
name := viper.GetString(client.FlagName)
res, err := builder.SignBuildBroadcast(name, msg, c.cdc)
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, c.cdc)
if err != nil {
return err
}
@ -77,7 +77,7 @@ func buildMsg(from sdk.Address) (sdk.Msg, error) {
}
to := sdk.Address(bz)
packet := ibc.NewIBCPacket(from, to, coins, client.FlagChainID,
packet := ibc.NewIBCPacket(from, to, coins, viper.GetString(client.FlagChainID),
viper.GetString(flagChain))
msg := ibc.IBCTransferMsg{

View File

@ -1,14 +1,15 @@
package commands
import (
"fmt"
"os"
"time"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
wire "github.com/cosmos/cosmos-sdk/wire"
@ -30,6 +31,8 @@ type relayCommander struct {
decoder sdk.AccountDecoder
mainStore string
ibcStore string
logger log.Logger
}
func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
@ -38,6 +41,8 @@ func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
decoder: authcmd.GetAccountDecoder(cdc),
ibcStore: "ibc",
mainStore: "main",
logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
}
cmd := &cobra.Command{
@ -68,7 +73,7 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) {
fromChainNode := viper.GetString(FlagFromChainNode)
toChainID := viper.GetString(FlagToChainID)
toChainNode := viper.GetString(FlagToChainNode)
address, err := builder.GetFromAddress()
address, err := context.NewCoreContextFromViper().GetFromAddress()
if err != nil {
panic(err)
}
@ -78,14 +83,17 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) {
}
func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) {
ctx := context.NewCoreContextFromViper()
// get password
name := viper.GetString(client.FlagName)
passphrase, err := builder.GetPassphraseFromStdin(name)
passphrase, err := ctx.GetPassphraseFromStdin(ctx.FromAddressName)
if err != nil {
panic(err)
}
ingressKey := ibc.IngressSequenceKey(fromChainID)
OUTER:
for {
time.Sleep(5 * time.Second)
processedbz, err := query(toChainNode, ingressKey, c.ibcStore)
if err != nil {
@ -99,14 +107,10 @@ func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode
panic(err)
}
OUTER:
for {
time.Sleep(time.Second)
lengthKey := ibc.EgressLengthKey(toChainID)
egressLengthbz, err := query(fromChainNode, lengthKey, c.ibcStore)
if err != nil {
fmt.Printf("Error querying outgoing packet list length: '%s'\n", err)
c.logger.Error("Error querying outgoing packet list length", "err", err)
continue OUTER
}
var egressLength int64
@ -115,43 +119,37 @@ OUTER:
} else if err = c.cdc.UnmarshalBinary(egressLengthbz, &egressLength); err != nil {
panic(err)
}
fmt.Printf("egressLength queried: %d\n", egressLength)
if egressLength > processed {
c.logger.Info("Detected IBC packet", "number", egressLength-1)
}
seq := c.getSequence(toChainNode)
for i := processed; i < egressLength; i++ {
egressbz, err := query(fromChainNode, ibc.EgressKey(toChainID, i), c.ibcStore)
if err != nil {
fmt.Printf("Error querying egress packet: '%s'\n", err)
c.logger.Error("Error querying egress packet", "err", err)
continue OUTER
}
err = c.broadcastTx(toChainNode, c.refine(egressbz, i, passphrase))
err = c.broadcastTx(seq, toChainNode, c.refine(egressbz, i, passphrase))
seq++
if err != nil {
fmt.Printf("Error broadcasting ingress packet: '%s'\n", err)
c.logger.Error("Error broadcasting ingress packet", "err", err)
continue OUTER
}
fmt.Printf("Relayed packet: %d\n", i)
c.logger.Info("Relayed IBC packet", "number", i)
}
processed = egressLength
}
}
func query(node string, key []byte, storeName string) (res []byte, err error) {
orig := viper.GetString(client.FlagNode)
viper.Set(client.FlagNode, node)
res, err = builder.Query(key, storeName)
viper.Set(client.FlagNode, orig)
return res, err
return context.NewCoreContextFromViper().WithNodeURI(node).Query(key, storeName)
}
func (c relayCommander) broadcastTx(node string, tx []byte) error {
orig := viper.GetString(client.FlagNode)
viper.Set(client.FlagNode, node)
seq := c.getSequence(node) + 1
viper.Set(client.FlagSequence, seq)
_, err := builder.BroadcastTx(tx)
viper.Set(client.FlagNode, orig)
func (c relayCommander) broadcastTx(seq int64, node string, tx []byte) error {
_, err := context.NewCoreContextFromViper().WithNodeURI(node).WithSequence(seq + 1).BroadcastTx(tx)
return err
}
@ -160,6 +158,7 @@ func (c relayCommander) getSequence(node string) int64 {
if err != nil {
panic(err)
}
account, err := c.decoder(res)
if err != nil {
panic(err)
@ -180,8 +179,8 @@ func (c relayCommander) refine(bz []byte, sequence int64, passphrase string) []b
Sequence: sequence,
}
name := viper.GetString(client.FlagName)
res, err := builder.SignAndBuild(name, passphrase, msg, c.cdc)
ctx := context.NewCoreContextFromViper()
res, err := ctx.SignAndBuild(ctx.FromAddressName, passphrase, msg, c.cdc)
if err != nil {
panic(err)
}

View File

@ -15,8 +15,6 @@ import (
"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/examples/basecoin/x/cool"
)
// AccountMapper(/CoinKeeper) and IBCMapper should use different StoreKey later
@ -53,8 +51,6 @@ func makeCodec() *wire.Codec {
struct{ sdk.Msg }{},
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
oldwire.ConcreteType{IBCTransferMsg{}, msgTypeIBCTransferMsg},
oldwire.ConcreteType{IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
)

14
x/ibc/rest/root.go Normal file
View File

@ -0,0 +1,14 @@
package rest
import (
"github.com/gorilla/mux"
keys "github.com/tendermint/go-crypto/keys"
"github.com/cosmos/cosmos-sdk/wire"
)
// RegisterRoutes - Central function to define routes that get registered by the main application
func RegisterRoutes(r *mux.Router, cdc *wire.Codec, kb keys.Keybase) {
r.HandleFunc("/ibc/{destchain}/{address}/send", TransferRequestHandler(cdc, kb)).Methods("POST")
}

98
x/ibc/rest/transfer.go Normal file
View File

@ -0,0 +1,98 @@
package rest
import (
"encoding/hex"
"encoding/json"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
"github.com/tendermint/go-crypto/keys"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/bank/commands"
"github.com/cosmos/cosmos-sdk/x/ibc"
)
type transferBody struct {
// Fees sdk.Coin `json="fees"`
Amount sdk.Coins `json:"amount"`
LocalAccountName string `json:"name"`
Password string `json:"password"`
SrcChainID string `json:"src_chain_id"`
Sequence int64 `json:"sequence"`
}
// TransferRequestHandler - http request handler to transfer coins to a address
// on a different chain via IBC
func TransferRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWriter, *http.Request) {
c := commands.Commander{cdc}
ctx := context.NewCoreContextFromViper()
return func(w http.ResponseWriter, r *http.Request) {
// collect data
vars := mux.Vars(r)
destChainID := vars["destchain"]
address := vars["address"]
var m transferBody
body, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
err = json.Unmarshal(body, &m)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
info, err := kb.Get(m.LocalAccountName)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(err.Error()))
return
}
bz, err := hex.DecodeString(address)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
to := sdk.Address(bz)
// build message
packet := ibc.NewIBCPacket(info.PubKey.Address(), to, m.Amount, m.SrcChainID, destChainID)
msg := ibc.IBCTransferMsg{packet}
// sign
ctx = ctx.WithSequence(m.Sequence)
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(err.Error()))
return
}
// send
res, err := ctx.BroadcastTx(txBytes)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
output, err := json.MarshalIndent(res, "", " ")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}

View File

@ -9,11 +9,10 @@ import (
crypto "github.com/tendermint/go-crypto"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/simplestake"
)
const (
@ -48,7 +47,9 @@ type commander struct {
}
func (co commander) bondTxCmd(cmd *cobra.Command, args []string) error {
from, err := builder.GetFromAddress()
ctx := context.NewCoreContextFromViper()
from, err := ctx.GetFromAddress()
if err != nil {
return err
}
@ -76,25 +77,25 @@ func (co commander) bondTxCmd(cmd *cobra.Command, args []string) error {
var pubKeyEd crypto.PubKeyEd25519
copy(pubKeyEd[:], rawPubKey)
msg := staking.NewBondMsg(from, stake, pubKeyEd.Wrap())
msg := simplestake.NewBondMsg(from, stake, pubKeyEd.Wrap())
return co.sendMsg(msg)
}
func (co commander) unbondTxCmd(cmd *cobra.Command, args []string) error {
from, err := builder.GetFromAddress()
from, err := context.NewCoreContextFromViper().GetFromAddress()
if err != nil {
return err
}
msg := staking.NewUnbondMsg(from)
msg := simplestake.NewUnbondMsg(from)
return co.sendMsg(msg)
}
func (co commander) sendMsg(msg sdk.Msg) error {
name := viper.GetString(client.FlagName)
res, err := builder.SignBuildBroadcast(name, msg, co.cdc)
ctx := context.NewCoreContextFromViper()
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, co.cdc)
if err != nil {
return err
}

View File

@ -1,11 +1,11 @@
package staking
package simplestake
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
const (
// Staking errors reserve 300 - 399.
// simplestake errors reserve 300 - 399.
CodeEmptyValidator sdk.CodeType = 300
CodeInvalidUnbond sdk.CodeType = 301
CodeEmptyStake sdk.CodeType = 302

View File

@ -1,4 +1,4 @@
package staking
package simplestake
import (
abci "github.com/tendermint/abci/types"
@ -6,7 +6,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// NewHandler returns a handler for "bank" type messages.
// NewHandler returns a handler for "simplestake" type messages.
func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {

View File

@ -1,4 +1,4 @@
package staking
package simplestake
import (
crypto "github.com/tendermint/go-crypto"
@ -10,6 +10,8 @@ import (
const stakingToken = "steak"
const moduleName = "simplestake"
type Keeper struct {
ck bank.CoinKeeper

View File

@ -1,4 +1,4 @@
package staking
package simplestake
import (
"fmt"

View File

@ -1,4 +1,4 @@
package staking
package simplestake
import (
"encoding/json"
@ -26,7 +26,7 @@ func NewBondMsg(addr sdk.Address, stake sdk.Coin, pubKey crypto.PubKey) BondMsg
}
func (msg BondMsg) Type() string {
return "staking"
return moduleName
}
func (msg BondMsg) ValidateBasic() sdk.Error {
@ -71,7 +71,7 @@ func NewUnbondMsg(addr sdk.Address) UnbondMsg {
}
func (msg UnbondMsg) Type() string {
return "staking"
return moduleName
}
func (msg UnbondMsg) ValidateBasic() sdk.Error {

View File

@ -1,4 +1,4 @@
package staking
package simplestake
import (
"testing"

View File

@ -1,4 +1,4 @@
package staking
package simplestake
import crypto "github.com/tendermint/go-crypto"

View File

@ -11,7 +11,7 @@ import (
crypto "github.com/tendermint/go-crypto"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire" // XXX fix
"github.com/cosmos/cosmos-sdk/x/stake"
@ -47,7 +47,8 @@ func GetCmdQueryCandidates(cdc *wire.Codec, storeName string) *cobra.Command {
key := PrefixedKey(stake.MsgType, stake.CandidatesKey)
res, err := builder.Query(key, storeName)
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(key, storeName)
if err != nil {
return err
}
@ -87,7 +88,9 @@ func GetCmdQueryCandidate(cdc *wire.Codec, storeName string) *cobra.Command {
key := PrefixedKey(stake.MsgType, stake.GetCandidateKey(addr))
res, err := builder.Query(key, storeName)
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(key, storeName)
if err != nil {
return err
}
@ -133,7 +136,9 @@ func GetCmdQueryDelegatorBond(cdc *wire.Codec, storeName string) *cobra.Command
key := PrefixedKey(stake.MsgType, stake.GetDelegatorBondKey(delegator, addr, cdc))
res, err := builder.Query(key, storeName)
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(key, storeName)
if err != nil {
return err
}
@ -175,7 +180,9 @@ func GetCmdQueryDelegatorBonds(cdc *wire.Codec, storeName string) *cobra.Command
key := PrefixedKey(stake.MsgType, stake.GetDelegatorBondsKey(delegator, cdc))
res, err := builder.Query(key, storeName)
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(key, storeName)
if err != nil {
return err
}

View File

@ -11,7 +11,7 @@ import (
crypto "github.com/tendermint/go-crypto"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/stake"
@ -92,8 +92,8 @@ func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command {
msg := stake.NewMsgDeclareCandidacy(candidateAddr, pk, amount, description)
// build and sign the transaction, then broadcast to Tendermint
name := viper.GetString(client.FlagName)
res, err := builder.SignBuildBroadcast(name, msg, cdc)
ctx := context.NewCoreContextFromViper()
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
if err != nil {
return err
}
@ -129,8 +129,8 @@ func GetCmdEditCandidacy(cdc *wire.Codec) *cobra.Command {
msg := stake.NewMsgEditCandidacy(candidateAddr, description)
// build and sign the transaction, then broadcast to Tendermint
name := viper.GetString(client.FlagName)
res, err := builder.SignBuildBroadcast(name, msg, cdc)
ctx := context.NewCoreContextFromViper()
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
if err != nil {
return err
}
@ -165,8 +165,8 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
msg := stake.NewMsgDelegate(delegatorAddr, candidateAddr, amount)
// build and sign the transaction, then broadcast to Tendermint
name := viper.GetString(client.FlagName)
res, err := builder.SignBuildBroadcast(name, msg, cdc)
ctx := context.NewCoreContextFromViper()
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
if err != nil {
return err
}
@ -212,8 +212,8 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
msg := stake.NewMsgUnbond(delegatorAddr, candidateAddr, sharesStr)
// build and sign the transaction, then broadcast to Tendermint
name := viper.GetString(client.FlagName)
res, err := builder.SignBuildBroadcast(name, msg, cdc)
ctx := context.NewCoreContextFromViper()
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
if err != nil {
return err
}

View File

@ -78,6 +78,9 @@ func ErrBadDelegatorAddr() sdk.Error {
func ErrCandidateExistsAddr() sdk.Error {
return newError(CodeInvalidValidator, "Candidate already exist, cannot re-declare candidacy")
}
func ErrCandidateRevoked() sdk.Error {
return newError(CodeInvalidValidator, "Candidacy for this address is currently revoked")
}
func ErrMissingSignature() sdk.Error {
return newError(CodeInvalidValidator, "Missing signature")
}

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