Merge branch 'develop' into fedekunze/README
This commit is contained in:
commit
56c3ae4a7f
26
CHANGELOG.md
26
CHANGELOG.md
|
@ -1,27 +1,31 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 0.13.0 (TBD)
|
## 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
|
BREAKING CHANGES
|
||||||
|
|
||||||
* [baseapp] `AddRoute` takes an `InitGenesis` function for per-module
|
|
||||||
initialization
|
|
||||||
* [basecoin] Remove cool/sketchy modules -> moved to new `democoin`
|
* [basecoin] Remove cool/sketchy modules -> moved to new `democoin`
|
||||||
* [basecoin] NewBasecoinApp takes a `map[string]dbm.DB` as temporary measure
|
* [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
|
to allow mounting multiple stores with their own DB until they can share one
|
||||||
* [staking] Renamed to `simplestake`
|
* [x/staking] Renamed to `simplestake`
|
||||||
* [builder] Functions don't take `passphrase` as argument
|
* [builder] Functions don't take `passphrase` as argument
|
||||||
* [server] GenAppState returns generated seed and address
|
* [server] GenAppState returns generated seed and address
|
||||||
* [basecoind] `init` command outputs JSON of everything necessary for testnet
|
* [basecoind] `init` command outputs JSON of everything necessary for testnet
|
||||||
* [basecoind] `basecoin.db -> data/basecoin.db`
|
* [basecoind] `basecoin.db -> data/basecoin.db`
|
||||||
* [basecli] `data/keys.db -> keys/keys.db`
|
* [basecli] `data/keys.db -> keys/keys.db`
|
||||||
* [cool] Mapper -> Keeper
|
|
||||||
|
|
||||||
FEATURES
|
FEATURES
|
||||||
|
|
||||||
* [types] `Coin` supports direct arithmetic operations
|
* [types] `Coin` supports direct arithmetic operations
|
||||||
* [basecoind] Add `show_validator` and `show_node_id` commands
|
* [basecoind] Add `show_validator` and `show_node_id` commands
|
||||||
* [staking] Initial merge of full staking module!
|
* [x/stake] Initial merge of full staking module!
|
||||||
* [democoin] New example application to demo custom modules
|
* [democoin] New example application to demo custom modules
|
||||||
|
|
||||||
IMPROVEMENTS
|
IMPROVEMENTS
|
||||||
|
@ -45,9 +49,9 @@ BREAKING CHANGES
|
||||||
* [types] Replace tx.GetFeePayer with FeePayer(tx) - returns the first signer
|
* [types] Replace tx.GetFeePayer with FeePayer(tx) - returns the first signer
|
||||||
* [types] NewStdTx takes the Fee
|
* [types] NewStdTx takes the Fee
|
||||||
* [types] ParseAccount -> AccountDecoder; ErrTxParse -> ErrTxDecoder
|
* [types] ParseAccount -> AccountDecoder; ErrTxParse -> ErrTxDecoder
|
||||||
* [auth] AnteHandler deducts fees
|
* [x/auth] AnteHandler deducts fees
|
||||||
* [bank] Move some errors to `types`
|
* [x/bank] Move some errors to `types`
|
||||||
* [bank] Remove sequence and signature from Input
|
* [x/bank] Remove sequence and signature from Input
|
||||||
|
|
||||||
FEATURES
|
FEATURES
|
||||||
|
|
||||||
|
@ -71,8 +75,8 @@ IMPROVEMENTS
|
||||||
* [specs] Staking
|
* [specs] Staking
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
* [auth] Fix setting pubkey on new account
|
* [x/auth] Fix setting pubkey on new account
|
||||||
* [auth] Require signatures to include the sequences
|
* [x/auth] Require signatures to include the sequences
|
||||||
* [baseapp] Dont panic on nil handler
|
* [baseapp] Dont panic on nil handler
|
||||||
* [basecoin] Check for empty bytes in account and tx
|
* [basecoin] Check for empty bytes in account and tx
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
"json/scanner",
|
"json/scanner",
|
||||||
"json/token"
|
"json/token"
|
||||||
]
|
]
|
||||||
revision = "f40e974e75af4e271d97ce0fc917af5898ae7bda"
|
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -191,8 +191,8 @@
|
||||||
".",
|
".",
|
||||||
"mem"
|
"mem"
|
||||||
]
|
]
|
||||||
revision = "bb8f1927f2a9d3ab41c9340aa034f6b803f4359c"
|
revision = "63644898a8da0bc22138abf860edaf5277b6102e"
|
||||||
version = "v1.0.2"
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/spf13/cast"
|
name = "github.com/spf13/cast"
|
||||||
|
@ -203,8 +203,8 @@
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/spf13/cobra"
|
name = "github.com/spf13/cobra"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
|
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
|
||||||
version = "v0.0.1"
|
version = "v0.0.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -250,7 +250,7 @@
|
||||||
"leveldb/table",
|
"leveldb/table",
|
||||||
"leveldb/util"
|
"leveldb/util"
|
||||||
]
|
]
|
||||||
revision = "169b1b37be738edb2813dab48c97a549bcf99bb5"
|
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/tendermint/abci"
|
name = "github.com/tendermint/abci"
|
||||||
|
@ -341,8 +341,8 @@
|
||||||
"version",
|
"version",
|
||||||
"wire"
|
"wire"
|
||||||
]
|
]
|
||||||
revision = "6f9956990c444d53f62f2a3905ed410cfe9afe77"
|
revision = "a1dd329d72e78d4770e602359bad5b7b1e8b72a3"
|
||||||
version = "v0.17.1"
|
version = "v0.18.0-rc1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/tendermint/tmlibs"
|
name = "github.com/tendermint/tmlibs"
|
||||||
|
@ -359,8 +359,8 @@
|
||||||
"pubsub",
|
"pubsub",
|
||||||
"pubsub/query"
|
"pubsub/query"
|
||||||
]
|
]
|
||||||
revision = "24da7009c3d8c019b40ba4287495749e3160caca"
|
revision = "2e24b64fc121dcdf1cabceab8dc2f7257675483c"
|
||||||
version = "v0.7.1"
|
version = "0.8.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -376,7 +376,7 @@
|
||||||
"ripemd160",
|
"ripemd160",
|
||||||
"salsa20/salsa"
|
"salsa20/salsa"
|
||||||
]
|
]
|
||||||
revision = "88942b9c40a4c9d203b82b3731787b672d6e809b"
|
revision = "b2aa35443fbc700ab74c586ae79b81c171851023"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -390,13 +390,13 @@
|
||||||
"lex/httplex",
|
"lex/httplex",
|
||||||
"trace"
|
"trace"
|
||||||
]
|
]
|
||||||
revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
|
revision = "b3c676e531a6dc479fa1b35ac961c13f5e2b4d2e"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = ["unix"]
|
packages = ["unix"]
|
||||||
revision = "13d03a9a82fba647c21a0ef8fba44a795d0f0835"
|
revision = "1d206c9fa8975fb4cf00df1dc8bf3283dc24ba0e"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "golang.org/x/text"
|
name = "golang.org/x/text"
|
||||||
|
@ -423,7 +423,7 @@
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "google.golang.org/genproto"
|
name = "google.golang.org/genproto"
|
||||||
packages = ["googleapis/rpc/status"]
|
packages = ["googleapis/rpc/status"]
|
||||||
revision = "ab0870e398d5dd054b868c0db1481ab029b9a9f2"
|
revision = "35de2414665fc36f56b72d982c5af480d86de5ab"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "google.golang.org/grpc"
|
name = "google.golang.org/grpc"
|
||||||
|
@ -452,12 +452,12 @@
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "gopkg.in/yaml.v2"
|
name = "gopkg.in/yaml.v2"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "86f5ed62f8a0ee96bd888d2efdfd6d4fb100a4eb"
|
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||||
version = "v2.2.0"
|
version = "v2.2.1"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "ed1f3f7f1728cd02945f90ca780e9bdc982573a36a5cc8d7e9f19fb40ba2ca19"
|
inputs-digest = "67298e1f8058b85f082dbd32123f2779b11bda282616e595141dba41a8675c39"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
|
@ -70,11 +70,11 @@
|
||||||
name = "github.com/tendermint/iavl"
|
name = "github.com/tendermint/iavl"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
version = "~0.17.1"
|
version = "~0.18.0-rc1"
|
||||||
name = "github.com/tendermint/tendermint"
|
name = "github.com/tendermint/tendermint"
|
||||||
|
|
||||||
[[constraint]]
|
[[override]]
|
||||||
version = "~0.7.1"
|
version = "~0.8.1"
|
||||||
name = "github.com/tendermint/tmlibs"
|
name = "github.com/tendermint/tmlibs"
|
||||||
|
|
||||||
[prune]
|
[prune]
|
||||||
|
|
26
Makefile
26
Makefile
|
@ -2,7 +2,7 @@ PACKAGES=$(shell go list ./... | grep -v '/vendor/')
|
||||||
COMMIT_HASH := $(shell git rev-parse --short HEAD)
|
COMMIT_HASH := $(shell git rev-parse --short HEAD)
|
||||||
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}"
|
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}"
|
||||||
|
|
||||||
all: check_tools get_vendor_deps build test
|
all: check_tools get_vendor_deps build build_examples test
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
### CI
|
### CI
|
||||||
|
@ -13,13 +13,16 @@ ci: get_tools get_vendor_deps build test_cover
|
||||||
### Build
|
### Build
|
||||||
|
|
||||||
# This can be unified later, here for easy demos
|
# 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:
|
build:
|
||||||
@rm -rf $(shell pwd)/examples/basecoin/vendor/
|
ifeq ($(OS),Windows_NT)
|
||||||
@rm -rf $(shell pwd)/examples/democoin/vendor/
|
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)
|
ifeq ($(OS),Windows_NT)
|
||||||
go build $(BUILD_FLAGS) -o build/basecoind.exe ./examples/basecoin/cmd/basecoind
|
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/basecli.exe ./examples/basecoin/cmd/basecli
|
||||||
|
@ -33,6 +36,10 @@ else
|
||||||
endif
|
endif
|
||||||
|
|
||||||
install:
|
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/basecoind
|
||||||
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecli
|
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecli
|
||||||
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democoind
|
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democoind
|
||||||
|
@ -84,12 +91,9 @@ test: test_unit # test_cli
|
||||||
# go test -coverprofile=c.out && go tool cover -html=c.out
|
# go test -coverprofile=c.out && go tool cover -html=c.out
|
||||||
|
|
||||||
test_unit:
|
test_unit:
|
||||||
@rm -rf examples/basecoin/vendor/
|
|
||||||
@rm -rf examples/democoin/vendor/
|
|
||||||
@go test $(PACKAGES)
|
@go test $(PACKAGES)
|
||||||
|
|
||||||
test_cover:
|
test_cover:
|
||||||
@rm -rf examples/basecoin/vendor/
|
|
||||||
@bash tests/test_cover.sh
|
@bash tests/test_cover.sh
|
||||||
|
|
||||||
benchmark:
|
benchmark:
|
||||||
|
@ -123,4 +127,4 @@ devdoc_update:
|
||||||
# To avoid unintended conflicts with file names, always add to .PHONY
|
# To avoid unintended conflicts with file names, always add to .PHONY
|
||||||
# unless there is a reason not to.
|
# unless there is a reason not to.
|
||||||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
||||||
.PHONY: build 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
|
||||||
|
|
|
@ -70,4 +70,4 @@ The key directories of the SDK are:
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
See the [documentation](https://cosmos-sdk.readthedocs.io).
|
See the [documentation](https://cosmos-sdk.readthedocs.io).
|
|
@ -248,11 +248,6 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
|
||||||
// TODO Return something intelligent
|
// TODO Return something intelligent
|
||||||
panic(err)
|
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
|
// NOTE: we don't commit, but BeginBlock for block 1
|
||||||
// starts from this deliverState
|
// starts from this deliverState
|
||||||
|
@ -266,7 +261,7 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||||
queryable, ok := app.cms.(sdk.Queryable)
|
queryable, ok := app.cms.(sdk.Queryable)
|
||||||
if !ok {
|
if !ok {
|
||||||
msg := "application doesn't support queries"
|
msg := "application doesn't support queries"
|
||||||
return sdk.ErrUnknownRequest(msg).Result().ToQuery()
|
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||||
}
|
}
|
||||||
return queryable.Query(req)
|
return queryable.Query(req)
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,7 +249,7 @@ func TestDeliverTx(t *testing.T) {
|
||||||
|
|
||||||
counter += 1
|
counter += 1
|
||||||
return sdk.Result{}
|
return sdk.Result{}
|
||||||
}, nil)
|
})
|
||||||
|
|
||||||
tx := testUpdatePowerTx{} // doesn't matter
|
tx := testUpdatePowerTx{} // doesn't matter
|
||||||
header := abci.Header{AppHash: []byte("apphash")}
|
header := abci.Header{AppHash: []byte("apphash")}
|
||||||
|
@ -284,7 +284,7 @@ func TestQuery(t *testing.T) {
|
||||||
store := ctx.KVStore(capKey)
|
store := ctx.KVStore(capKey)
|
||||||
store.Set(key, value)
|
store.Set(key, value)
|
||||||
return sdk.Result{}
|
return sdk.Result{}
|
||||||
}, nil)
|
})
|
||||||
|
|
||||||
query := abci.RequestQuery{
|
query := abci.RequestQuery{
|
||||||
Path: "/main/key",
|
Path: "/main/key",
|
||||||
|
@ -349,7 +349,7 @@ func TestValidatorChange(t *testing.T) {
|
||||||
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||||
// TODO
|
// TODO
|
||||||
return sdk.Result{}
|
return sdk.Result{}
|
||||||
}, nil)
|
})
|
||||||
|
|
||||||
// Load latest state, which should be empty.
|
// Load latest state, which should be empty.
|
||||||
err := app.LoadLatestVersion(capKey)
|
err := app.LoadLatestVersion(capKey)
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package baseapp
|
package baseapp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
@ -10,16 +8,14 @@ import (
|
||||||
|
|
||||||
// Router provides handlers for each transaction type.
|
// Router provides handlers for each transaction type.
|
||||||
type Router interface {
|
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)
|
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
|
// map a transaction type to a handler and an initgenesis function
|
||||||
type route struct {
|
type route struct {
|
||||||
r string
|
r string
|
||||||
h sdk.Handler
|
h sdk.Handler
|
||||||
i sdk.InitGenesis
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type router struct {
|
type router struct {
|
||||||
|
@ -38,11 +34,11 @@ func NewRouter() *router {
|
||||||
var isAlpha = regexp.MustCompile(`^[a-zA-Z]+$`).MatchString
|
var isAlpha = regexp.MustCompile(`^[a-zA-Z]+$`).MatchString
|
||||||
|
|
||||||
// AddRoute - TODO add description
|
// 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) {
|
if !isAlpha(r) {
|
||||||
panic("route expressions can only contain alphanumeric characters")
|
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
|
return rtr
|
||||||
}
|
}
|
||||||
|
@ -57,20 +53,3 @@ func (rtr *router) Route(path string) (h sdk.Handler) {
|
||||||
}
|
}
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -1,10 +1,9 @@
|
||||||
package builder
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||||
|
@ -17,9 +16,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Broadcast the transaction bytes to Tendermint
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -43,17 +42,17 @@ func BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query from Tendermint with the provided key and storename
|
// 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)
|
path := fmt.Sprintf("/%s/key", storeName)
|
||||||
node, err := client.GetNode()
|
node, err := ctx.GetNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := rpcclient.ABCIQueryOptions{
|
opts := rpcclient.ABCIQueryOptions{
|
||||||
Height: viper.GetInt64(client.FlagHeight),
|
Height: ctx.Height,
|
||||||
Trusted: viper.GetBool(client.FlagTrustNode),
|
Trusted: ctx.TrustNode,
|
||||||
}
|
}
|
||||||
result, err := node.ABCIQueryWithOptions(path, key, opts)
|
result, err := node.ABCIQueryWithOptions(path, key, opts)
|
||||||
if err != nil {
|
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
|
// 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()
|
keybase, err := keys.GetKeyBase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
name := viper.GetString(client.FlagName)
|
name := ctx.FromAddressName
|
||||||
if name == "" {
|
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)
|
info, err := keybase.Get(name)
|
||||||
|
@ -88,11 +87,11 @@ func GetFromAddress() (from sdk.Address, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sign and build the transaction from the msg
|
// 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
|
// build the Sign Messsage from the Standard Message
|
||||||
chainID := viper.GetString(client.FlagChainID)
|
chainID := ctx.ChainID
|
||||||
sequence := int64(viper.GetInt(client.FlagSequence))
|
sequence := ctx.Sequence
|
||||||
signMsg := sdk.StdSignMsg{
|
signMsg := sdk.StdSignMsg{
|
||||||
ChainID: chainID,
|
ChainID: chainID,
|
||||||
Sequences: []int64{sequence},
|
Sequences: []int64{sequence},
|
||||||
|
@ -114,7 +113,7 @@ func SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *wire.Codec) ([]byte
|
||||||
sigs := []sdk.StdSignature{{
|
sigs := []sdk.StdSignature{{
|
||||||
PubKey: pubkey,
|
PubKey: pubkey,
|
||||||
Signature: sig,
|
Signature: sig,
|
||||||
Sequence: viper.GetInt64(client.FlagSequence),
|
Sequence: sequence,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// marshal bytes
|
// 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
|
// sign and build the transaction from the msg
|
||||||
func SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (*ctypes.ResultBroadcastTxCommit, error) {
|
func (ctx CoreContext) SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||||
passphrase, err := GetPassphraseFromStdin(name)
|
passphrase, err := ctx.GetPassphraseFromStdin(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
txBytes, err := SignAndBuild(name, passphrase, msg, cdc)
|
txBytes, err := ctx.SignAndBuild(name, passphrase, msg, cdc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return BroadcastTx(txBytes)
|
return ctx.BroadcastTx(txBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get passphrase from std input
|
// 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()
|
buf := client.BufferStdin()
|
||||||
prompt := fmt.Sprintf("Password to sign with '%s':", name)
|
prompt := fmt.Sprintf("Password to sign with '%s':", name)
|
||||||
return client.GetPassword(prompt, buf)
|
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
|
||||||
|
}
|
|
@ -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
|
|
||||||
}
|
|
|
@ -158,7 +158,7 @@ func TestNodeStatus(t *testing.T) {
|
||||||
|
|
||||||
func TestBlock(t *testing.T) {
|
func TestBlock(t *testing.T) {
|
||||||
|
|
||||||
time.Sleep(time.Second * 2) // TODO: LOL -> wait for blocks
|
waitForHeight(2)
|
||||||
|
|
||||||
var resultBlock ctypes.ResultBlock
|
var resultBlock ctypes.ResultBlock
|
||||||
|
|
||||||
|
@ -222,8 +222,7 @@ func TestCoinSend(t *testing.T) {
|
||||||
|
|
||||||
// create TX
|
// create TX
|
||||||
receiveAddr, resultTx := doSend(t, port, seed)
|
receiveAddr, resultTx := doSend(t, port, seed)
|
||||||
|
waitForHeight(resultTx.Height + 1)
|
||||||
time.Sleep(time.Second * 2) // T
|
|
||||||
|
|
||||||
// check if tx was commited
|
// check if tx was commited
|
||||||
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||||
|
@ -258,7 +257,7 @@ func TestIBCTransfer(t *testing.T) {
|
||||||
// create TX
|
// create TX
|
||||||
resultTx := doIBCTransfer(t, port, seed)
|
resultTx := doIBCTransfer(t, port, seed)
|
||||||
|
|
||||||
time.Sleep(time.Second * 2) // T
|
waitForHeight(resultTx.Height + 1)
|
||||||
|
|
||||||
// check if tx was commited
|
// check if tx was commited
|
||||||
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||||
|
@ -296,7 +295,7 @@ func TestTxs(t *testing.T) {
|
||||||
// create TX
|
// create TX
|
||||||
_, resultTx := doSend(t, port, seed)
|
_, resultTx := doSend(t, port, seed)
|
||||||
|
|
||||||
time.Sleep(time.Second * 2) // TO
|
waitForHeight(resultTx.Height + 1)
|
||||||
|
|
||||||
// check if tx is findable
|
// check if tx is findable
|
||||||
res, body := request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
|
res, body := request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
|
||||||
|
@ -392,7 +391,7 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(time.Second * 2)
|
waitForStart()
|
||||||
|
|
||||||
return node, lcd, nil
|
return node, lcd, nil
|
||||||
}
|
}
|
||||||
|
@ -442,6 +441,7 @@ func request(t *testing.T, port, method, path string, payload []byte) (*http.Res
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
output, err := ioutil.ReadAll(res.Body)
|
output, err := ioutil.ReadAll(res.Body)
|
||||||
|
res.Body.Close()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
return res, string(output)
|
return res, string(output)
|
||||||
|
@ -461,8 +461,6 @@ func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctype
|
||||||
acc := auth.BaseAccount{}
|
acc := auth.BaseAccount{}
|
||||||
err = json.Unmarshal([]byte(body), &acc)
|
err = json.Unmarshal([]byte(body), &acc)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
fmt.Println("BODY", body)
|
|
||||||
fmt.Println("ACC", acc)
|
|
||||||
sequence := acc.Sequence
|
sequence := acc.Sequence
|
||||||
|
|
||||||
// send
|
// send
|
||||||
|
@ -490,8 +488,6 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
|
||||||
acc := auth.BaseAccount{}
|
acc := auth.BaseAccount{}
|
||||||
err = json.Unmarshal([]byte(body), &acc)
|
err = json.Unmarshal([]byte(body), &acc)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
fmt.Println("BODY", body)
|
|
||||||
fmt.Println("ACC", acc)
|
|
||||||
sequence := acc.Sequence
|
sequence := acc.Sequence
|
||||||
|
|
||||||
// send
|
// send
|
||||||
|
@ -504,3 +500,72 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
|
||||||
|
|
||||||
return resultTx
|
return resultTx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func waitForHeight(height int64) {
|
||||||
|
for {
|
||||||
|
var resultBlock ctypes.ResultBlock
|
||||||
|
|
||||||
|
url := fmt.Sprintf("http://localhost:%v%v", port, "/blocks/latest")
|
||||||
|
res, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
res.Body.Close()
|
||||||
|
|
||||||
|
err = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -31,7 +32,8 @@ func blockCommand() *cobra.Command {
|
||||||
|
|
||||||
func getBlock(height *int64) ([]byte, error) {
|
func getBlock(height *int64) ([]byte, error) {
|
||||||
// get the node
|
// get the node
|
||||||
node, err := client.GetNode()
|
ctx := context.NewCoreContextFromViper()
|
||||||
|
node, err := ctx.GetNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -55,7 +57,7 @@ func getBlock(height *int64) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetChainHeight() (int64, error) {
|
func GetChainHeight() (int64, error) {
|
||||||
node, err := client.GetNode()
|
node, err := context.NewCoreContextFromViper().GetNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ func statusCommand() *cobra.Command {
|
||||||
|
|
||||||
func getNodeStatus() (*ctypes.ResultStatus, error) {
|
func getNodeStatus() (*ctypes.ResultStatus, error) {
|
||||||
// get the node
|
// get the node
|
||||||
node, err := client.GetNode()
|
node, err := context.NewCoreContextFromViper().GetNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &ctypes.ResultStatus{}, err
|
return &ctypes.ResultStatus{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
func validatorCommand() *cobra.Command {
|
func validatorCommand() *cobra.Command {
|
||||||
|
@ -26,7 +27,7 @@ func validatorCommand() *cobra.Command {
|
||||||
|
|
||||||
func GetValidators(height *int64) ([]byte, error) {
|
func GetValidators(height *int64) ([]byte, error) {
|
||||||
// get the node
|
// get the node
|
||||||
node, err := client.GetNode()
|
node, err := context.NewCoreContextFromViper().GetNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BroadcastTxBody struct {
|
type BroadcastTxBody struct {
|
||||||
|
@ -22,7 +22,7 @@ func BroadcastTxRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := builder.BroadcastTx([]byte(m.TxBytes))
|
res, err := context.NewCoreContextFromViper().BroadcastTx([]byte(m.TxBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
)
|
)
|
||||||
|
@ -39,7 +40,7 @@ func (c commander) queryTx(hashHexStr string, trustNode bool) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the node
|
// get the node
|
||||||
node, err := client.GetNode()
|
node, err := context.NewCoreContextFromViper().GetNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,7 +44,7 @@ func (c commander) searchTx(tags []string) ([]byte, error) {
|
||||||
query := strings.Join(tags, " AND ")
|
query := strings.Join(tags, " AND ")
|
||||||
|
|
||||||
// get the node
|
// get the node
|
||||||
node, err := client.GetNode()
|
node, err := context.NewCoreContextFromViper().GetNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@ const (
|
||||||
flagFee = "fee"
|
flagFee = "fee"
|
||||||
)
|
)
|
||||||
|
|
||||||
// gaiacliCmd is the entry point for this binary
|
// rootCmd is the entry point for this binary
|
||||||
var (
|
var (
|
||||||
gaiacliCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "gaiacli",
|
Use: "gaiacli",
|
||||||
Short: "Gaia light-client",
|
Short: "Gaia light-client",
|
||||||
}
|
}
|
||||||
|
@ -54,16 +54,16 @@ func main() {
|
||||||
cobra.EnableCommandSorting = false
|
cobra.EnableCommandSorting = false
|
||||||
|
|
||||||
// generic client commands
|
// generic client commands
|
||||||
AddClientCommands(gaiacliCmd)
|
AddClientCommands(rootCmd)
|
||||||
// query commands (custom to binary)
|
// query commands (custom to binary)
|
||||||
gaiacliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
GetCommands(getAccountCmd)...)
|
GetCommands(getAccountCmd)...)
|
||||||
// post tx commands (custom to binary)
|
// post tx commands (custom to binary)
|
||||||
gaiacliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
PostCommands(postSendCommand())...)
|
PostCommands(postSendCommand())...)
|
||||||
|
|
||||||
// add proxy, version and key info
|
// add proxy, version and key info
|
||||||
gaiacliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
lineBreak,
|
lineBreak,
|
||||||
serveCommand(),
|
serveCommand(),
|
||||||
KeyCommands(),
|
KeyCommands(),
|
||||||
|
@ -72,6 +72,6 @@ func main() {
|
||||||
)
|
)
|
||||||
|
|
||||||
// prepare and add flags
|
// prepare and add flags
|
||||||
executor := cli.PrepareBaseCmd(gaiacliCmd, "GA", os.ExpandEnv("$HOME/.gaiacli"))
|
executor := cli.PrepareBaseCmd(rootCmd, "GA", os.ExpandEnv("$HOME/.gaiacli"))
|
||||||
executor.Execute()
|
executor.Execute()
|
||||||
}
|
}
|
|
@ -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()
|
||||||
|
}
|
|
@ -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.
|
|
|
@ -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
|
|
|
@ -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.
|
|
||||||
|
|
|
@ -61,9 +61,9 @@ func NewBasecoinApp(logger log.Logger, dbs map[string]dbm.DB) *BasecoinApp {
|
||||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
||||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
||||||
app.Router().
|
app.Router().
|
||||||
AddRoute("bank", bank.NewHandler(coinKeeper), nil).
|
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper), nil).
|
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
||||||
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper), nil)
|
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
|
||||||
|
|
||||||
// initialize BaseApp
|
// initialize BaseApp
|
||||||
app.SetTxDecoder(app.txDecoder)
|
app.SetTxDecoder(app.txDecoder)
|
||||||
|
|
|
@ -36,6 +36,7 @@ var (
|
||||||
addr4 = priv4.PubKey().Address()
|
addr4 = priv4.PubKey().Address()
|
||||||
coins = sdk.Coins{{"foocoin", 10}}
|
coins = sdk.Coins{{"foocoin", 10}}
|
||||||
halfCoins = sdk.Coins{{"foocoin", 5}}
|
halfCoins = sdk.Coins{{"foocoin", 5}}
|
||||||
|
manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}}
|
||||||
fee = sdk.StdFee{
|
fee = sdk.StdFee{
|
||||||
sdk.Coins{{"foocoin", 0}},
|
sdk.Coins{{"foocoin", 0}},
|
||||||
0,
|
0,
|
||||||
|
@ -73,6 +74,15 @@ var (
|
||||||
bank.NewOutput(addr1, coins),
|
bank.NewOutput(addr1, coins),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendMsg5 = bank.SendMsg{
|
||||||
|
Inputs: []bank.Input{
|
||||||
|
bank.NewInput(addr1, manyCoins),
|
||||||
|
},
|
||||||
|
Outputs: []bank.Output{
|
||||||
|
bank.NewOutput(addr2, manyCoins),
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func loggerAndDBs() (log.Logger, map[string]dbm.DB) {
|
func loggerAndDBs() (log.Logger, map[string]dbm.DB) {
|
||||||
|
@ -131,6 +141,48 @@ func TestMsgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func TestGenesis(t *testing.T) {
|
||||||
logger, dbs := loggerAndDBs()
|
logger, dbs := loggerAndDBs()
|
||||||
bapp := NewBasecoinApp(logger, dbs)
|
bapp := NewBasecoinApp(logger, dbs)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -24,18 +23,14 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
"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 (
|
var (
|
||||||
basecliCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "basecli",
|
Use: "basecli",
|
||||||
Short: "Basecoin light-client",
|
Short: "Basecoin light-client",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func todoNotImplemented(_ *cobra.Command, _ []string) error {
|
|
||||||
return errors.New("TODO: Command not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// disable sorting
|
// disable sorting
|
||||||
cobra.EnableCommandSorting = false
|
cobra.EnableCommandSorting = false
|
||||||
|
@ -48,36 +43,36 @@ func main() {
|
||||||
// with the cdc
|
// with the cdc
|
||||||
|
|
||||||
// add standard rpc, and tx commands
|
// add standard rpc, and tx commands
|
||||||
rpc.AddCommands(basecliCmd)
|
rpc.AddCommands(rootCmd)
|
||||||
basecliCmd.AddCommand(client.LineBreak)
|
rootCmd.AddCommand(client.LineBreak)
|
||||||
tx.AddCommands(basecliCmd, cdc)
|
tx.AddCommands(rootCmd, cdc)
|
||||||
basecliCmd.AddCommand(client.LineBreak)
|
rootCmd.AddCommand(client.LineBreak)
|
||||||
|
|
||||||
// add query/post commands (custom to binary)
|
// add query/post commands (custom to binary)
|
||||||
basecliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
client.GetCommands(
|
client.GetCommands(
|
||||||
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
|
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
|
||||||
)...)
|
)...)
|
||||||
basecliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
client.PostCommands(
|
client.PostCommands(
|
||||||
bankcmd.SendTxCmd(cdc),
|
bankcmd.SendTxCmd(cdc),
|
||||||
)...)
|
)...)
|
||||||
basecliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
client.PostCommands(
|
client.PostCommands(
|
||||||
ibccmd.IBCTransferCmd(cdc),
|
ibccmd.IBCTransferCmd(cdc),
|
||||||
)...)
|
)...)
|
||||||
basecliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
client.PostCommands(
|
client.PostCommands(
|
||||||
ibccmd.IBCRelayCmd(cdc),
|
ibccmd.IBCRelayCmd(cdc),
|
||||||
simplestakingcmd.BondTxCmd(cdc),
|
simplestakingcmd.BondTxCmd(cdc),
|
||||||
)...)
|
)...)
|
||||||
basecliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
client.PostCommands(
|
client.PostCommands(
|
||||||
simplestakingcmd.UnbondTxCmd(cdc),
|
simplestakingcmd.UnbondTxCmd(cdc),
|
||||||
)...)
|
)...)
|
||||||
|
|
||||||
// add proxy, version and key info
|
// add proxy, version and key info
|
||||||
basecliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
lcd.ServeCommand(cdc),
|
lcd.ServeCommand(cdc),
|
||||||
keys.Commands(),
|
keys.Commands(),
|
||||||
|
@ -86,6 +81,6 @@ func main() {
|
||||||
)
|
)
|
||||||
|
|
||||||
// prepare and add flags
|
// prepare and add flags
|
||||||
executor := cli.PrepareMainCmd(basecliCmd, "BC", os.ExpandEnv("$HOME/.basecli"))
|
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.basecli"))
|
||||||
executor.Execute()
|
executor.Execute()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
@ -10,58 +8,38 @@ import (
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/tmlibs/cli"
|
"github.com/tendermint/tmlibs/cli"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
|
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
"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 (
|
var (
|
||||||
basecoindCmd = &cobra.Command{
|
context = server.NewDefaultContext()
|
||||||
Use: "gaiad",
|
rootCmd = &cobra.Command{
|
||||||
Short: "Gaia Daemon (server)",
|
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, string, cmn.HexBytes, error) {
|
|
||||||
addr, secret, err := server.GenerateCoinKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", nil, err
|
|
||||||
}
|
|
||||||
opts := fmt.Sprintf(`{
|
|
||||||
"accounts": [{
|
|
||||||
"address": "%s",
|
|
||||||
"coins": [
|
|
||||||
{
|
|
||||||
"denom": "mycoin",
|
|
||||||
"amount": 9007199254740992
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
}`, addr)
|
|
||||||
return json.RawMessage(opts), secret, addr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||||
dbMain, err := dbm.NewGoLevelDB("basecoin", filepath.Join(rootDir, "data"))
|
dataDir := filepath.Join(rootDir, "data")
|
||||||
|
dbMain, err := dbm.NewGoLevelDB("basecoin", dataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dbAcc, err := dbm.NewGoLevelDB("basecoin-acc", filepath.Join(rootDir, "data"))
|
dbAcc, err := dbm.NewGoLevelDB("basecoin-acc", dataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dbIBC, err := dbm.NewGoLevelDB("basecoin-ibc", filepath.Join(rootDir, "data"))
|
dbIBC, err := dbm.NewGoLevelDB("basecoin-ibc", dataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dbStaking, err := dbm.NewGoLevelDB("basecoin-staking", filepath.Join(rootDir, "data"))
|
dbStaking, err := dbm.NewGoLevelDB("basecoin-staking", dataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -76,21 +54,10 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// TODO: set logger through CLI
|
server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context)
|
||||||
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,
|
|
||||||
)
|
|
||||||
|
|
||||||
// prepare and add flags
|
// prepare and add flags
|
||||||
rootDir := os.ExpandEnv("$HOME/.basecoind")
|
rootDir := os.ExpandEnv("$HOME/.basecoind")
|
||||||
executor := cli.PrepareBaseCmd(basecoindCmd, "BC", rootDir)
|
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
|
||||||
executor.Execute()
|
executor.Execute()
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
|
||||||
return &GenesisAccount{
|
return &GenesisAccount{
|
||||||
Name: aa.Name,
|
Name: aa.Name,
|
||||||
Address: aa.Address,
|
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) {
|
func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) {
|
||||||
baseAcc := auth.BaseAccount{
|
baseAcc := auth.BaseAccount{
|
||||||
Address: ga.Address,
|
Address: ga.Address,
|
||||||
Coins: ga.Coins,
|
Coins: ga.Coins.Sort(),
|
||||||
}
|
}
|
||||||
return &AppAccount{
|
return &AppAccount{
|
||||||
BaseAccount: baseAcc,
|
BaseAccount: baseAcc,
|
||||||
|
|
|
@ -1,204 +0,0 @@
|
||||||
Cosmos-SDK Democoin (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.
|
|
|
@ -1,22 +0,0 @@
|
||||||
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
|
|
||||||
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/examples/democoin/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/democoin ./cmd/...
|
|
||||||
|
|
||||||
get_vendor_deps:
|
|
||||||
@rm -rf vendor/
|
|
||||||
@dep ensure
|
|
||||||
|
|
||||||
test:
|
|
||||||
@go test $(PACKAGES)
|
|
||||||
|
|
||||||
benchmark:
|
|
||||||
@go test -bench=. $(PACKAGES)
|
|
||||||
|
|
||||||
.PHONY: all build test benchmark
|
|
|
@ -1,70 +0,0 @@
|
||||||
# Democoin
|
|
||||||
|
|
||||||
This is the "Democoin" example application built on the Cosmos-Sdk. This
|
|
||||||
"Democoin" is not affiliated with [Coinbase](http://www.getdemocoin.com/), nor
|
|
||||||
the [stable coin](http://www.getdemocoin.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 `democoind` and `basecli`
|
|
||||||
binaries.
|
|
||||||
|
|
||||||
If you want to create a new application, start by copying the Democoin app.
|
|
||||||
|
|
||||||
|
|
||||||
# Building your own Blockchain
|
|
||||||
|
|
||||||
Democoin 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/democoin`
|
|
||||||
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 Democoin
|
|
||||||
|
|
||||||
Democoin is build with the cosmos-sdk. It is a sample application that works
|
|
||||||
with any engine that implements the ABCI protocol. Democoin defines multiple
|
|
||||||
unique modules as well as uses modules directly from the sdk. If you want
|
|
||||||
to modify Democoin, 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.
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
||||||
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/sketchy"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/sketchy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ type DemocoinApp struct {
|
||||||
// keys to access the substores
|
// keys to access the substores
|
||||||
capKeyMainStore *sdk.KVStoreKey
|
capKeyMainStore *sdk.KVStoreKey
|
||||||
capKeyAccountStore *sdk.KVStoreKey
|
capKeyAccountStore *sdk.KVStoreKey
|
||||||
|
capKeyPowStore *sdk.KVStoreKey
|
||||||
capKeyIBCStore *sdk.KVStoreKey
|
capKeyIBCStore *sdk.KVStoreKey
|
||||||
capKeyStakingStore *sdk.KVStoreKey
|
capKeyStakingStore *sdk.KVStoreKey
|
||||||
|
|
||||||
|
@ -48,6 +50,7 @@ func NewDemocoinApp(logger log.Logger, dbs map[string]dbm.DB) *DemocoinApp {
|
||||||
cdc: MakeCodec(),
|
cdc: MakeCodec(),
|
||||||
capKeyMainStore: sdk.NewKVStoreKey("main"),
|
capKeyMainStore: sdk.NewKVStoreKey("main"),
|
||||||
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
|
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
|
||||||
|
capKeyPowStore: sdk.NewKVStoreKey("pow"),
|
||||||
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
|
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
|
||||||
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
|
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
|
||||||
}
|
}
|
||||||
|
@ -61,20 +64,23 @@ func NewDemocoinApp(logger log.Logger, dbs map[string]dbm.DB) *DemocoinApp {
|
||||||
// add handlers
|
// add handlers
|
||||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
||||||
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
|
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
|
||||||
|
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper)
|
||||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
||||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
||||||
app.Router().
|
app.Router().
|
||||||
AddRoute("bank", bank.NewHandler(coinKeeper), nil).
|
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||||
AddRoute("cool", cool.NewHandler(coolKeeper), coolKeeper.InitGenesis).
|
AddRoute("cool", cool.NewHandler(coolKeeper)).
|
||||||
AddRoute("sketchy", sketchy.NewHandler(), nil).
|
AddRoute("pow", powKeeper.Handler).
|
||||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper), nil).
|
AddRoute("sketchy", sketchy.NewHandler()).
|
||||||
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper), nil)
|
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
||||||
|
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
|
||||||
|
|
||||||
// initialize BaseApp
|
// initialize BaseApp
|
||||||
app.SetTxDecoder(app.txDecoder)
|
app.SetTxDecoder(app.txDecoder)
|
||||||
app.SetInitChainer(app.initChainer)
|
app.SetInitChainer(app.initChainerFn(coolKeeper, powKeeper))
|
||||||
app.MountStoreWithDB(app.capKeyMainStore, sdk.StoreTypeIAVL, dbs["main"])
|
app.MountStoreWithDB(app.capKeyMainStore, sdk.StoreTypeIAVL, dbs["main"])
|
||||||
app.MountStoreWithDB(app.capKeyAccountStore, sdk.StoreTypeIAVL, dbs["acc"])
|
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.capKeyIBCStore, sdk.StoreTypeIAVL, dbs["ibc"])
|
||||||
app.MountStoreWithDB(app.capKeyStakingStore, sdk.StoreTypeIAVL, dbs["staking"])
|
app.MountStoreWithDB(app.capKeyStakingStore, sdk.StoreTypeIAVL, dbs["staking"])
|
||||||
// NOTE: Broken until #532 lands
|
// NOTE: Broken until #532 lands
|
||||||
|
@ -95,16 +101,18 @@ func MakeCodec() *wire.Codec {
|
||||||
const msgTypeIssue = 0x2
|
const msgTypeIssue = 0x2
|
||||||
const msgTypeQuiz = 0x3
|
const msgTypeQuiz = 0x3
|
||||||
const msgTypeSetTrend = 0x4
|
const msgTypeSetTrend = 0x4
|
||||||
const msgTypeIBCTransferMsg = 0x5
|
const msgTypeMine = 0x5
|
||||||
const msgTypeIBCReceiveMsg = 0x6
|
const msgTypeIBCTransferMsg = 0x6
|
||||||
const msgTypeBondMsg = 0x7
|
const msgTypeIBCReceiveMsg = 0x7
|
||||||
const msgTypeUnbondMsg = 0x8
|
const msgTypeBondMsg = 0x8
|
||||||
|
const msgTypeUnbondMsg = 0x9
|
||||||
var _ = oldwire.RegisterInterface(
|
var _ = oldwire.RegisterInterface(
|
||||||
struct{ sdk.Msg }{},
|
struct{ sdk.Msg }{},
|
||||||
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
|
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
|
||||||
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
|
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
|
||||||
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
|
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
|
||||||
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
|
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
|
||||||
|
oldwire.ConcreteType{pow.MineMsg{}, msgTypeMine},
|
||||||
oldwire.ConcreteType{ibc.IBCTransferMsg{}, msgTypeIBCTransferMsg},
|
oldwire.ConcreteType{ibc.IBCTransferMsg{}, msgTypeIBCTransferMsg},
|
||||||
oldwire.ConcreteType{ibc.IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
|
oldwire.ConcreteType{ibc.IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
|
||||||
oldwire.ConcreteType{simplestake.BondMsg{}, msgTypeBondMsg},
|
oldwire.ConcreteType{simplestake.BondMsg{}, msgTypeBondMsg},
|
||||||
|
@ -143,23 +151,39 @@ func (app *DemocoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom logic for democoin initialization
|
// custom logic for democoin initialization
|
||||||
func (app *DemocoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keeper) sdk.InitChainer {
|
||||||
stateJSON := req.AppStateBytes
|
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||||
|
stateJSON := req.AppStateBytes
|
||||||
|
|
||||||
genesisState := new(types.GenesisState)
|
genesisState := new(types.GenesisState)
|
||||||
err := json.Unmarshal(stateJSON, genesisState)
|
err := json.Unmarshal(stateJSON, genesisState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, gacc := range genesisState.Accounts {
|
for _, gacc := range genesisState.Accounts {
|
||||||
acc, err := gacc.ToAppAccount()
|
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 {
|
if err != nil {
|
||||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||||
}
|
}
|
||||||
app.accountMapper.SetAccount(ctx, acc)
|
|
||||||
|
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{}
|
||||||
}
|
}
|
||||||
return abci.ResponseInitChain{}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
||||||
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
@ -71,6 +72,7 @@ func loggerAndDBs() (log.Logger, map[string]dbm.DB) {
|
||||||
dbs := map[string]dbm.DB{
|
dbs := map[string]dbm.DB{
|
||||||
"main": dbm.NewMemDB(),
|
"main": dbm.NewMemDB(),
|
||||||
"acc": dbm.NewMemDB(),
|
"acc": dbm.NewMemDB(),
|
||||||
|
"pow": dbm.NewMemDB(),
|
||||||
"ibc": dbm.NewMemDB(),
|
"ibc": dbm.NewMemDB(),
|
||||||
"staking": dbm.NewMemDB(),
|
"staking": dbm.NewMemDB(),
|
||||||
}
|
}
|
||||||
|
@ -238,6 +240,58 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
||||||
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
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) {
|
func TestQuizMsg(t *testing.T) {
|
||||||
bapp := newDemocoinApp()
|
bapp := newDemocoinApp()
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -22,20 +21,18 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/types"
|
||||||
|
coolcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/cool/commands"
|
||||||
|
powcmd "github.com/cosmos/cosmos-sdk/examples/democoin/x/pow/commands"
|
||||||
)
|
)
|
||||||
|
|
||||||
// gaiacliCmd is the entry point for this binary
|
// rootCmd is the entry point for this binary
|
||||||
var (
|
var (
|
||||||
democliCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "democli",
|
Use: "democli",
|
||||||
Short: "Democoin light-client",
|
Short: "Democoin light-client",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func todoNotImplemented(_ *cobra.Command, _ []string) error {
|
|
||||||
return errors.New("TODO: Command not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// disable sorting
|
// disable sorting
|
||||||
cobra.EnableCommandSorting = false
|
cobra.EnableCommandSorting = false
|
||||||
|
@ -48,36 +45,44 @@ func main() {
|
||||||
// with the cdc
|
// with the cdc
|
||||||
|
|
||||||
// add standard rpc, and tx commands
|
// add standard rpc, and tx commands
|
||||||
rpc.AddCommands(democliCmd)
|
rpc.AddCommands(rootCmd)
|
||||||
democliCmd.AddCommand(client.LineBreak)
|
rootCmd.AddCommand(client.LineBreak)
|
||||||
tx.AddCommands(democliCmd, cdc)
|
tx.AddCommands(rootCmd, cdc)
|
||||||
democliCmd.AddCommand(client.LineBreak)
|
rootCmd.AddCommand(client.LineBreak)
|
||||||
|
|
||||||
// add query/post commands (custom to binary)
|
// add query/post commands (custom to binary)
|
||||||
democliCmd.AddCommand(
|
// start with commands common to basecoin
|
||||||
|
rootCmd.AddCommand(
|
||||||
client.GetCommands(
|
client.GetCommands(
|
||||||
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
|
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
|
||||||
)...)
|
)...)
|
||||||
democliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
client.PostCommands(
|
client.PostCommands(
|
||||||
bankcmd.SendTxCmd(cdc),
|
bankcmd.SendTxCmd(cdc),
|
||||||
)...)
|
)...)
|
||||||
democliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
client.PostCommands(
|
client.PostCommands(
|
||||||
ibccmd.IBCTransferCmd(cdc),
|
ibccmd.IBCTransferCmd(cdc),
|
||||||
)...)
|
)...)
|
||||||
democliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
client.PostCommands(
|
client.PostCommands(
|
||||||
ibccmd.IBCRelayCmd(cdc),
|
ibccmd.IBCRelayCmd(cdc),
|
||||||
simplestakingcmd.BondTxCmd(cdc),
|
simplestakingcmd.BondTxCmd(cdc),
|
||||||
)...)
|
)...)
|
||||||
democliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
client.PostCommands(
|
client.PostCommands(
|
||||||
simplestakingcmd.UnbondTxCmd(cdc),
|
simplestakingcmd.UnbondTxCmd(cdc),
|
||||||
)...)
|
)...)
|
||||||
|
// and now democoin specific commands
|
||||||
|
rootCmd.AddCommand(
|
||||||
|
client.PostCommands(
|
||||||
|
coolcmd.QuizTxCmd(cdc),
|
||||||
|
coolcmd.SetTrendTxCmd(cdc),
|
||||||
|
powcmd.MineCmd(cdc),
|
||||||
|
)...)
|
||||||
|
|
||||||
// add proxy, version and key info
|
// add proxy, version and key info
|
||||||
democliCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
lcd.ServeCommand(cdc),
|
lcd.ServeCommand(cdc),
|
||||||
keys.Commands(),
|
keys.Commands(),
|
||||||
|
@ -86,6 +91,6 @@ func main() {
|
||||||
)
|
)
|
||||||
|
|
||||||
// prepare and add flags
|
// prepare and add flags
|
||||||
executor := cli.PrepareMainCmd(democliCmd, "BC", os.ExpandEnv("$HOME/.democli"))
|
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.democli"))
|
||||||
executor.Execute()
|
executor.Execute()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
@ -10,45 +9,41 @@ import (
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/tmlibs/cli"
|
"github.com/tendermint/tmlibs/cli"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
"github.com/cosmos/cosmos-sdk/version"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// democoindCmd is the entry point for this binary
|
// rootCmd is the entry point for this binary
|
||||||
var (
|
var (
|
||||||
democoindCmd = &cobra.Command{
|
context = server.NewDefaultContext()
|
||||||
Use: "democoind",
|
rootCmd = &cobra.Command{
|
||||||
Short: "Gaia Daemon (server)",
|
Use: "democoind",
|
||||||
|
Short: "Democoin Daemon (server)",
|
||||||
|
PersistentPreRunE: server.PersistentPreRunEFn(context),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// defaultOptions sets up the app_options for the
|
// defaultAppState sets up the app_state for the
|
||||||
// default genesis file
|
// default genesis file
|
||||||
func defaultOptions(args []string) (json.RawMessage, string, cmn.HexBytes, error) {
|
func defaultAppState(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
|
||||||
addr, secret, err := server.GenerateCoinKey()
|
baseJSON, err := server.DefaultGenAppState(args, addr, coinDenom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fmt.Println("Secret phrase to access coins:")
|
var jsonMap map[string]json.RawMessage
|
||||||
fmt.Println(secret)
|
err = json.Unmarshal(baseJSON, &jsonMap)
|
||||||
|
if err != nil {
|
||||||
opts := fmt.Sprintf(`{
|
return nil, err
|
||||||
"accounts": [{
|
}
|
||||||
"address": "%s",
|
jsonMap["cool"] = json.RawMessage(`{
|
||||||
"coins": [
|
"trend": "ice-cold"
|
||||||
{
|
}`)
|
||||||
"denom": "mycoin",
|
bz, err := json.Marshal(jsonMap)
|
||||||
"amount": 9007199254740992
|
return json.RawMessage(bz), err
|
||||||
}
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
}`, addr)
|
|
||||||
return json.RawMessage(opts), "", nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||||
|
@ -60,6 +55,10 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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"))
|
dbIBC, err := dbm.NewGoLevelDB("democoin-ibc", filepath.Join(rootDir, "data"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -71,6 +70,7 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||||
dbs := map[string]dbm.DB{
|
dbs := map[string]dbm.DB{
|
||||||
"main": dbMain,
|
"main": dbMain,
|
||||||
"acc": dbAcc,
|
"acc": dbAcc,
|
||||||
|
"pow": dbPow,
|
||||||
"ibc": dbIBC,
|
"ibc": dbIBC,
|
||||||
"staking": dbStaking,
|
"staking": dbStaking,
|
||||||
}
|
}
|
||||||
|
@ -79,21 +79,10 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// TODO: set logger through CLI
|
server.AddCommands(rootCmd, defaultAppState, generateApp, context)
|
||||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
|
|
||||||
With("module", "main")
|
|
||||||
|
|
||||||
democoindCmd.AddCommand(
|
|
||||||
server.InitCmd(defaultOptions, logger),
|
|
||||||
server.StartCmd(generateApp, logger),
|
|
||||||
server.UnsafeResetAllCmd(logger),
|
|
||||||
server.ShowNodeIdCmd(logger),
|
|
||||||
server.ShowValidatorCmd(logger),
|
|
||||||
version.VersionCmd,
|
|
||||||
)
|
|
||||||
|
|
||||||
// prepare and add flags
|
// prepare and add flags
|
||||||
rootDir := os.ExpandEnv("$HOME/.democoind")
|
rootDir := os.ExpandEnv("$HOME/.democoind")
|
||||||
executor := cli.PrepareBaseCmd(democoindCmd, "BC", rootDir)
|
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
|
||||||
executor.Execute()
|
executor.Execute()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"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)
|
var _ sdk.Account = (*AppAccount)(nil)
|
||||||
|
@ -41,7 +44,9 @@ func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder {
|
||||||
|
|
||||||
// State to Unmarshal
|
// State to Unmarshal
|
||||||
type GenesisState struct {
|
type GenesisState struct {
|
||||||
Accounts []*GenesisAccount `json:"accounts"`
|
Accounts []*GenesisAccount `json:"accounts"`
|
||||||
|
PowGenesis pow.PowGenesis `json:"pow"`
|
||||||
|
CoolGenesis cool.CoolGenesis `json:"cool"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenesisAccount doesn't need pubkey or sequence
|
// GenesisAccount doesn't need pubkey or sequence
|
||||||
|
@ -55,7 +60,7 @@ func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
|
||||||
return &GenesisAccount{
|
return &GenesisAccount{
|
||||||
Name: aa.Name,
|
Name: aa.Name,
|
||||||
Address: aa.Address,
|
Address: aa.Address,
|
||||||
Coins: aa.Coins,
|
Coins: aa.Coins.Sort(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +68,7 @@ func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
|
||||||
func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) {
|
func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) {
|
||||||
baseAcc := auth.BaseAccount{
|
baseAcc := auth.BaseAccount{
|
||||||
Address: ga.Address,
|
Address: ga.Address,
|
||||||
Coins: ga.Coins,
|
Coins: ga.Coins.Sort(),
|
||||||
}
|
}
|
||||||
return &AppAccount{
|
return &AppAccount{
|
||||||
BaseAccount: baseAcc,
|
BaseAccount: baseAcc,
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
||||||
|
@ -24,8 +24,10 @@ func QuizTxCmd(cdc *wire.Codec) *cobra.Command {
|
||||||
return errors.New("You must provide an answer")
|
return errors.New("You must provide an answer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx := context.NewCoreContextFromViper()
|
||||||
|
|
||||||
// get the from address from the name flag
|
// get the from address from the name flag
|
||||||
from, err := builder.GetFromAddress()
|
from, err := ctx.GetFromAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -37,7 +39,7 @@ func QuizTxCmd(cdc *wire.Codec) *cobra.Command {
|
||||||
name := viper.GetString(client.FlagName)
|
name := viper.GetString(client.FlagName)
|
||||||
|
|
||||||
// build and sign the transaction, then broadcast to Tendermint
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -58,8 +60,10 @@ func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command {
|
||||||
return errors.New("You must provide an answer")
|
return errors.New("You must provide an answer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx := context.NewCoreContextFromViper()
|
||||||
|
|
||||||
// get the from address from the name flag
|
// get the from address from the name flag
|
||||||
from, err := builder.GetFromAddress()
|
from, err := ctx.GetFromAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -71,7 +75,7 @@ func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command {
|
||||||
msg := cool.NewSetTrendMsg(from, args[0])
|
msg := cool.NewSetTrendMsg(from, args[0])
|
||||||
|
|
||||||
// build and sign the transaction, then broadcast to Tendermint
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,10 @@
|
||||||
package cool
|
package cool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"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
|
// Keeper - handlers sets/gets of custom variables for your module
|
||||||
type Keeper struct {
|
type Keeper struct {
|
||||||
ck bank.CoinKeeper
|
ck bank.CoinKeeper
|
||||||
|
@ -49,11 +42,7 @@ func (k Keeper) CheckTrend(ctx sdk.Context, guessedTrend string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitGenesis - store the genesis trend
|
// InitGenesis - store the genesis trend
|
||||||
func (k Keeper) InitGenesis(ctx sdk.Context, data json.RawMessage) error {
|
func (k Keeper) InitGenesis(ctx sdk.Context, data CoolGenesis) error {
|
||||||
var state GenesisState
|
k.setTrend(ctx, data.Trend)
|
||||||
if err := json.Unmarshal(data, &state); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
k.setTrend(ctx, state.trend)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,11 @@ type SetTrendMsg struct {
|
||||||
Cool string
|
Cool string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Genesis state - specify genesis trend
|
||||||
|
type CoolGenesis struct {
|
||||||
|
Trend string `json:"trend"`
|
||||||
|
}
|
||||||
|
|
||||||
// New cool message
|
// New cool message
|
||||||
func NewSetTrendMsg(sender sdk.Address, cool string) SetTrendMsg {
|
func NewSetTrendMsg(sender sdk.Address, cool string) SetTrendMsg {
|
||||||
return SetTrendMsg{
|
return SetTrendMsg{
|
||||||
|
|
|
@ -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 count, 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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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{}
|
||||||
|
}
|
|
@ -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{})
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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]")
|
||||||
|
}
|
|
@ -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.
|
|
|
@ -1,72 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tmlibs/cli"
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
|
||||||
"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, string, cmn.HexBytes, error) {
|
|
||||||
addr, secret, err := server.GenerateCoinKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", 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), secret, addr, 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()
|
|
||||||
}
|
|
|
@ -41,7 +41,7 @@ func main() {
|
||||||
baseApp.SetTxDecoder(decodeTx)
|
baseApp.SetTxDecoder(decodeTx)
|
||||||
|
|
||||||
// Set a handler Route.
|
// Set a handler Route.
|
||||||
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore), nil)
|
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore))
|
||||||
|
|
||||||
// Load latest version.
|
// Load latest version.
|
||||||
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
|
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||||
baseApp.SetInitChainer(InitChainer(capKeyMainStore))
|
baseApp.SetInitChainer(InitChainer(capKeyMainStore))
|
||||||
|
|
||||||
// Set a handler Route.
|
// Set a handler Route.
|
||||||
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore), nil)
|
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore))
|
||||||
|
|
||||||
// Load latest version.
|
// Load latest version.
|
||||||
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
|
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
|
||||||
|
@ -107,7 +106,7 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci
|
||||||
// GenInitOptions can be passed into InitCmd,
|
// GenInitOptions can be passed into InitCmd,
|
||||||
// returns a static string of a few key-values that can be parsed
|
// returns a static string of a few key-values that can be parsed
|
||||||
// by InitChainer
|
// by InitChainer
|
||||||
func GenInitOptions(args []string) (json.RawMessage, string, cmn.HexBytes, error) {
|
func GenInitOptions(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
|
||||||
opts := []byte(`{
|
opts := []byte(`{
|
||||||
"values": [
|
"values": [
|
||||||
{
|
{
|
||||||
|
@ -120,5 +119,5 @@ func GenInitOptions(args []string) (json.RawMessage, string, cmn.HexBytes, error
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`)
|
}`)
|
||||||
return opts, "", nil, nil
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ func TestInitApp(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// initialize it future-way
|
// initialize it future-way
|
||||||
opts, _, _, err := GenInitOptions(nil)
|
opts, err := GenInitOptions(nil, nil, "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
req := abci.RequestInitChain{AppStateBytes: opts}
|
req := abci.RequestInitChain{AppStateBytes: opts}
|
||||||
app.InitChain(req)
|
app.InitChain(req)
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
*/
|
|
118
server/init.go
118
server/init.go
|
@ -5,33 +5,43 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
"github.com/tendermint/go-crypto/keys"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/go-crypto/keys/words"
|
||||||
|
|
||||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
|
||||||
cfg "github.com/tendermint/tendermint/config"
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
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 {
|
type testnetInformation struct {
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
|
|
||||||
|
ChainID string `json:"chain_id"`
|
||||||
Account string `json:"account"`
|
Account string `json:"account"`
|
||||||
Validator tmtypes.GenesisValidator `json:"validator"`
|
Validator tmtypes.GenesisValidator `json:"validator"`
|
||||||
NodeID p2p.ID `json:"node_id"`
|
NodeID p2p.ID `json:"node_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type initCmd struct {
|
||||||
|
genAppState GenAppState
|
||||||
|
context *Context
|
||||||
|
}
|
||||||
|
|
||||||
// InitCmd will initialize all files for tendermint,
|
// InitCmd will initialize all files for tendermint,
|
||||||
// along with proper app_state.
|
// along with proper app_state.
|
||||||
// The application can pass in a function to generate
|
// The application can pass in a function to generate
|
||||||
// proper state. And may want to use GenerateCoinKey
|
// proper state. And may want to use GenerateCoinKey
|
||||||
// to create default account(s).
|
// to create default account(s).
|
||||||
func InitCmd(gen GenAppState, logger log.Logger) *cobra.Command {
|
func InitCmd(gen GenAppState, ctx *Context) *cobra.Command {
|
||||||
cmd := initCmd{
|
cmd := initCmd{
|
||||||
genAppState: gen,
|
genAppState: gen,
|
||||||
logger: logger,
|
context: ctx,
|
||||||
}
|
}
|
||||||
cobraCmd := cobra.Command{
|
cobraCmd := cobra.Command{
|
||||||
Use: "init",
|
Use: "init",
|
||||||
|
@ -41,28 +51,14 @@ func InitCmd(gen GenAppState, logger log.Logger) *cobra.Command {
|
||||||
return &cobraCmd
|
return &cobraCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenAppState can parse command-line to
|
|
||||||
// generate default app_state for the genesis file.
|
|
||||||
// Also must return generated seed and address
|
|
||||||
// This is application-specific
|
|
||||||
type GenAppState func(args []string) (json.RawMessage, string, cmn.HexBytes, error)
|
|
||||||
|
|
||||||
type initCmd struct {
|
|
||||||
genAppState GenAppState
|
|
||||||
logger log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c initCmd) run(cmd *cobra.Command, args []string) error {
|
func (c initCmd) run(cmd *cobra.Command, args []string) error {
|
||||||
// Store testnet information as we go
|
// Store testnet information as we go
|
||||||
var testnetInfo testnetInformation
|
var testnetInfo testnetInformation
|
||||||
|
|
||||||
// Run the basic tendermint initialization,
|
// Run the basic tendermint initialization,
|
||||||
// set up a default genesis with no app_options
|
// set up a default genesis with no app_options
|
||||||
config, err := tcmd.ParseConfig()
|
config := c.context.Config
|
||||||
if err != nil {
|
err := c.initTendermintFiles(config, &testnetInfo)
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = c.initTendermintFiles(config, &testnetInfo)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -72,14 +68,22 @@ func (c initCmd) run(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Now, we want to add the custom app_state
|
||||||
appState, secret, address, err := c.genAppState(args)
|
appState, err := c.genAppState(args, addr, DEFAULT_DENOM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
testnetInfo.Secret = secret
|
testnetInfo.Secret = secret
|
||||||
testnetInfo.Account = address.String()
|
testnetInfo.Account = addr.String()
|
||||||
|
|
||||||
// And add them to the genesis file
|
// And add them to the genesis file
|
||||||
genFile := config.GenesisFile()
|
genFile := config.GenesisFile()
|
||||||
|
@ -108,17 +112,17 @@ func (c initCmd) initTendermintFiles(config *cfg.Config, info *testnetInformatio
|
||||||
var privValidator *tmtypes.PrivValidatorFS
|
var privValidator *tmtypes.PrivValidatorFS
|
||||||
if cmn.FileExists(privValFile) {
|
if cmn.FileExists(privValFile) {
|
||||||
privValidator = tmtypes.LoadPrivValidatorFS(privValFile)
|
privValidator = tmtypes.LoadPrivValidatorFS(privValFile)
|
||||||
c.logger.Info("Found private validator", "path", privValFile)
|
c.context.Logger.Info("Found private validator", "path", privValFile)
|
||||||
} else {
|
} else {
|
||||||
privValidator = tmtypes.GenPrivValidatorFS(privValFile)
|
privValidator = tmtypes.GenPrivValidatorFS(privValFile)
|
||||||
privValidator.Save()
|
privValidator.Save()
|
||||||
c.logger.Info("Generated private validator", "path", privValFile)
|
c.context.Logger.Info("Generated private validator", "path", privValFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// genesis file
|
// genesis file
|
||||||
genFile := config.GenesisFile()
|
genFile := config.GenesisFile()
|
||||||
if cmn.FileExists(genFile) {
|
if cmn.FileExists(genFile) {
|
||||||
c.logger.Info("Found genesis file", "path", genFile)
|
c.context.Logger.Info("Found genesis file", "path", genFile)
|
||||||
} else {
|
} else {
|
||||||
genDoc := tmtypes.GenesisDoc{
|
genDoc := tmtypes.GenesisDoc{
|
||||||
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
|
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
|
||||||
|
@ -131,7 +135,7 @@ func (c initCmd) initTendermintFiles(config *cfg.Config, info *testnetInformatio
|
||||||
if err := genDoc.SaveAs(genFile); err != nil {
|
if err := genDoc.SaveAs(genFile); err != nil {
|
||||||
return err
|
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
|
// reload the config file and find our validator info
|
||||||
|
@ -144,10 +148,39 @@ func (c initCmd) initTendermintFiles(config *cfg.Config, info *testnetInformatio
|
||||||
info.Validator = validator
|
info.Validator = validator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
info.ChainID = loadedDoc.ChainID
|
||||||
|
|
||||||
return nil
|
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
|
// GenesisDoc involves some tendermint-specific structures we don't
|
||||||
// want to parse, so we just grab it into a raw object format,
|
// want to parse, so we just grab it into a raw object format,
|
||||||
// so we can add one line.
|
// so we can add one line.
|
||||||
|
@ -173,3 +206,30 @@ func addGenesisState(filename string, appState json.RawMessage) error {
|
||||||
|
|
||||||
return ioutil.WriteFile(filename, out, 0600)
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -8,13 +8,17 @@ import (
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/mock"
|
"github.com/cosmos/cosmos-sdk/mock"
|
||||||
|
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
defer setupViper(t)()
|
defer setupViper(t)()
|
||||||
|
|
||||||
logger := log.NewNopLogger()
|
logger := log.NewNopLogger()
|
||||||
cmd := InitCmd(mock.GenInitOptions, logger)
|
cfg, err := tcmd.ParseConfig()
|
||||||
err := cmd.RunE(nil, nil)
|
require.Nil(t, err)
|
||||||
|
ctx := NewContext(cfg, logger)
|
||||||
|
cmd := InitCmd(mock.GenInitOptions, ctx)
|
||||||
|
err = cmd.RunE(nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -21,16 +21,16 @@ const (
|
||||||
flagAddress = "address"
|
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
|
// 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
|
// StartCmd runs the service passed in, either
|
||||||
// stand-alone, or in-process with tendermint
|
// 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{
|
start := startCmd{
|
||||||
appCreator: app,
|
appCreator: app,
|
||||||
logger: logger,
|
context: ctx,
|
||||||
}
|
}
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "start",
|
Use: "start",
|
||||||
|
@ -48,16 +48,16 @@ func StartCmd(app appCreator, logger log.Logger) *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
type startCmd struct {
|
type startCmd struct {
|
||||||
appCreator appCreator
|
appCreator AppCreator
|
||||||
logger log.Logger
|
context *Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s startCmd) run(cmd *cobra.Command, args []string) error {
|
func (s startCmd) run(cmd *cobra.Command, args []string) error {
|
||||||
if !viper.GetBool(flagWithTendermint) {
|
if !viper.GetBool(flagWithTendermint) {
|
||||||
s.logger.Info("Starting ABCI without Tendermint")
|
s.context.Logger.Info("Starting ABCI without Tendermint")
|
||||||
return s.startStandAlone()
|
return s.startStandAlone()
|
||||||
}
|
}
|
||||||
s.logger.Info("Starting ABCI with Tendermint")
|
s.context.Logger.Info("Starting ABCI with Tendermint")
|
||||||
return s.startInProcess()
|
return s.startInProcess()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ func (s startCmd) startStandAlone() error {
|
||||||
// Generate the app in the proper dir
|
// Generate the app in the proper dir
|
||||||
addr := viper.GetString(flagAddress)
|
addr := viper.GetString(flagAddress)
|
||||||
home := viper.GetString("home")
|
home := viper.GetString("home")
|
||||||
app, err := s.appCreator(home, s.logger)
|
app, err := s.appCreator(home, s.context.Logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ func (s startCmd) startStandAlone() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("Error creating listener: %v\n", err)
|
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()
|
svr.Start()
|
||||||
|
|
||||||
// Wait forever
|
// Wait forever
|
||||||
|
@ -86,13 +86,9 @@ func (s startCmd) startStandAlone() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s startCmd) startInProcess() error {
|
func (s startCmd) startInProcess() error {
|
||||||
cfg, err := tcmd.ParseConfig()
|
cfg := s.context.Config
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
home := cfg.RootDir
|
home := cfg.RootDir
|
||||||
app, err := s.appCreator(home, s.logger)
|
app, err := s.appCreator(home, s.context.Logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -103,7 +99,7 @@ func (s startCmd) startInProcess() error {
|
||||||
proxy.NewLocalClientCreator(app),
|
proxy.NewLocalClientCreator(app),
|
||||||
node.DefaultGenesisDocProviderFunc(cfg),
|
node.DefaultGenesisDocProviderFunc(cfg),
|
||||||
node.DefaultDBProvider,
|
node.DefaultDBProvider,
|
||||||
s.logger.With("module", "node"))
|
s.context.Logger.With("module", "node"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// "os"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -9,47 +10,56 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/mock"
|
"github.com/cosmos/cosmos-sdk/mock"
|
||||||
|
"github.com/tendermint/abci/server"
|
||||||
|
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStartStandAlone(t *testing.T) {
|
func TestStartStandAlone(t *testing.T) {
|
||||||
defer setupViper(t)()
|
home, err := ioutil.TempDir("", "mock-sdk-cmd")
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(home)
|
||||||
|
}()
|
||||||
|
|
||||||
logger := log.NewNopLogger()
|
logger := log.NewNopLogger()
|
||||||
initCmd := InitCmd(mock.GenInitOptions, logger)
|
cfg, err := tcmd.ParseConfig()
|
||||||
err := initCmd.RunE(nil, nil)
|
require.Nil(t, err)
|
||||||
|
ctx := NewContext(cfg, logger)
|
||||||
|
initCmd := InitCmd(mock.GenInitOptions, ctx)
|
||||||
|
err = initCmd.RunE(nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// set up app and start up
|
app, err := mock.NewApp(home, logger)
|
||||||
viper.Set(flagWithTendermint, false)
|
require.Nil(t, err)
|
||||||
viper.Set(flagAddress, "localhost:11122")
|
svr, err := server.NewServer(FreeTCPAddr(t), "socket", app)
|
||||||
startCmd := StartCmd(mock.NewApp, logger)
|
require.Nil(t, err, "Error creating listener")
|
||||||
startCmd.Flags().Set(flagAddress, FreeTCPAddr(t)) // set to a new free address
|
svr.SetLogger(logger.With("module", "abci-server"))
|
||||||
timeout := time.Duration(3) * time.Second
|
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) {
|
func TestStartWithTendermint(t *testing.T) {
|
||||||
defer setupViper(t)()
|
defer setupViper(t)()
|
||||||
|
|
||||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
|
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
|
||||||
With("module", "mock-cmd")
|
With("module", "mock-cmd")
|
||||||
// logger := log.NewNopLogger()
|
cfg, err := tcmd.ParseConfig()
|
||||||
initCmd := InitCmd(mock.GenInitOptions, logger)
|
require.Nil(t, err)
|
||||||
err := initCmd.RunE(nil, nil)
|
ctx := NewContext(cfg, logger)
|
||||||
|
initCmd := InitCmd(mock.GenInitOptions, ctx)
|
||||||
|
err = initCmd.RunE(nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// set up app and start up
|
// set up app and start up
|
||||||
viper.Set(flagWithTendermint, true)
|
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
|
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)
|
close(RunOrTimeout(startCmd, timeout, t))
|
||||||
//panic(a)
|
|
||||||
|
|
||||||
RunOrTimeout(startCmd, timeout, t)
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||||
"github.com/tendermint/tmlibs/cli"
|
"github.com/tendermint/tmlibs/cli"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
@ -44,14 +45,18 @@ func setupViper(t *testing.T) func() {
|
||||||
func StartServer(t *testing.T) chan error {
|
func StartServer(t *testing.T) chan error {
|
||||||
defer setupViper(t)()
|
defer setupViper(t)()
|
||||||
|
|
||||||
|
cfg, err := tcmd.ParseConfig()
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
// init server
|
// init server
|
||||||
initCmd := InitCmd(mock.GenInitOptions, log.NewNopLogger())
|
ctx := NewContext(cfg, log.NewNopLogger())
|
||||||
err := initCmd.RunE(nil, nil)
|
initCmd := InitCmd(mock.GenInitOptions, ctx)
|
||||||
|
err = initCmd.RunE(nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// start server
|
// start server
|
||||||
viper.Set(flagWithTendermint, true)
|
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(flagAddress, FreeTCPAddr(t)) // set to a new free address
|
||||||
startCmd.Flags().Set("rpc.laddr", 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
|
timeout := time.Duration(3) * time.Second
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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,
|
||||||
|
)
|
||||||
|
}
|
|
@ -141,7 +141,7 @@ func (st *iavlStore) ReverseIterator(start, end []byte) Iterator {
|
||||||
func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||||
if len(req.Data) == 0 {
|
if len(req.Data) == 0 {
|
||||||
msg := "Query cannot be zero length"
|
msg := "Query cannot be zero length"
|
||||||
return sdk.ErrTxDecode(msg).Result().ToQuery()
|
return sdk.ErrTxDecode(msg).QueryResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
tree := st.tree
|
tree := st.tree
|
||||||
|
@ -175,7 +175,7 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||||
|
|
||||||
default:
|
default:
|
||||||
msg := fmt.Sprintf("Unexpected Query path: %v", req.Path)
|
msg := fmt.Sprintf("Unexpected Query path: %v", req.Path)
|
||||||
return sdk.ErrUnknownRequest(msg).Result().ToQuery()
|
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,18 +205,18 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery {
|
||||||
path := req.Path
|
path := req.Path
|
||||||
storeName, subpath, err := parsePath(path)
|
storeName, subpath, err := parsePath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Result().ToQuery()
|
return err.QueryResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
store := rs.getStoreByName(storeName)
|
store := rs.getStoreByName(storeName)
|
||||||
if store == nil {
|
if store == nil {
|
||||||
msg := fmt.Sprintf("no such store: %s", storeName)
|
msg := fmt.Sprintf("no such store: %s", storeName)
|
||||||
return sdk.ErrUnknownRequest(msg).Result().ToQuery()
|
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||||
}
|
}
|
||||||
queryable, ok := store.(Queryable)
|
queryable, ok := store.(Queryable)
|
||||||
if !ok {
|
if !ok {
|
||||||
msg := fmt.Sprintf("store %s doesn't support queries", storeName)
|
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
|
// trim the path and make the query
|
||||||
|
|
|
@ -257,7 +257,10 @@ func (coins Coins) Swap(i, j int) { coins[i], coins[j] = coins[j], coins[i]
|
||||||
var _ sort.Interface = Coins{}
|
var _ sort.Interface = Coins{}
|
||||||
|
|
||||||
// Sort is a helper function to sort the set of coins inplace
|
// 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
|
// Parsing
|
||||||
|
|
|
@ -3,6 +3,8 @@ package types
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ABCI Response Code
|
// ABCI Response Code
|
||||||
|
@ -121,6 +123,7 @@ type Error interface {
|
||||||
TraceCause(cause error, msg string) Error
|
TraceCause(cause error, msg string) Error
|
||||||
Cause() error
|
Cause() error
|
||||||
Result() Result
|
Result() Result
|
||||||
|
QueryResult() abci.ResponseQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewError(code CodeType, msg string) Error {
|
func NewError(code CodeType, msg string) Error {
|
||||||
|
@ -220,3 +223,11 @@ func (err *sdkError) Result() Result {
|
||||||
Log: err.ABCILog(),
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -38,11 +38,3 @@ type Result struct {
|
||||||
func (res Result) IsOK() bool {
|
func (res Result) IsOK() bool {
|
||||||
return res.Code.IsOK()
|
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ var (
|
||||||
func getVersion() string {
|
func getVersion() string {
|
||||||
v := Version
|
v := Version
|
||||||
if GitCommit != "" {
|
if GitCommit != "" {
|
||||||
v = v + " " + GitCommit
|
v = v + "-" + GitCommit
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,9 @@ package version
|
||||||
|
|
||||||
const Maj = "0"
|
const Maj = "0"
|
||||||
const Min = "13"
|
const Min = "13"
|
||||||
const Fix = "0"
|
const Fix = "2"
|
||||||
|
|
||||||
const Version = "0.13.0-dev"
|
const Version = "0.13.2-dev"
|
||||||
|
|
||||||
// GitCommit set by build flags
|
// GitCommit set by build flags
|
||||||
var GitCommit = ""
|
var GitCommit = ""
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"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"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"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)
|
key := sdk.Address(bz)
|
||||||
|
|
||||||
res, err := builder.Query(key, c.storeName)
|
ctx := context.NewCoreContextFromViper()
|
||||||
|
|
||||||
|
res, err := ctx.Query(key, c.storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"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"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"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) {
|
func QueryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder sdk.AccountDecoder) func(http.ResponseWriter, *http.Request) {
|
||||||
c := commander{storeName, cdc, decoder}
|
c := commander{storeName, cdc, decoder}
|
||||||
|
ctx := context.NewCoreContextFromViper()
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
addr := vars["address"]
|
addr := vars["address"]
|
||||||
|
@ -33,7 +34,7 @@ func QueryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder sdk.A
|
||||||
}
|
}
|
||||||
key := sdk.Address(bz)
|
key := sdk.Address(bz)
|
||||||
|
|
||||||
res, err := builder.Query(key, c.storeName)
|
res, err := ctx.Query(key, c.storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error())))
|
w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error())))
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
@ -38,8 +38,10 @@ type Commander struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Commander) sendTxCmd(cmd *cobra.Command, args []string) error {
|
func (c Commander) sendTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := context.NewCoreContextFromViper()
|
||||||
|
|
||||||
// get the from address
|
// get the from address
|
||||||
from, err := builder.GetFromAddress()
|
from, err := ctx.GetFromAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -59,14 +61,11 @@ func (c Commander) sendTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
to := sdk.Address(bz)
|
to := sdk.Address(bz)
|
||||||
|
|
||||||
// get account name
|
|
||||||
name := viper.GetString(client.FlagName)
|
|
||||||
|
|
||||||
// build message
|
// build message
|
||||||
msg := BuildMsg(from, to, coins)
|
msg := BuildMsg(from, to, coins)
|
||||||
|
|
||||||
// build and sign the transaction, then broadcast to Tendermint
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ func (out Output) ValidateBasic() sdk.Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (out Output) String() string {
|
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
|
// NewOutput - create a transaction output, used with SendMsg
|
||||||
|
|
|
@ -179,8 +179,10 @@ func TestSendMsgValidation(t *testing.T) {
|
||||||
|
|
||||||
func TestSendMsgString(t *testing.T) {
|
func TestSendMsgString(t *testing.T) {
|
||||||
// Construct a SendMsg
|
// Construct a SendMsg
|
||||||
addr1 := sdk.Address([]byte("input"))
|
addr1String := "input"
|
||||||
addr2 := sdk.Address([]byte("output"))
|
addr2String := "output"
|
||||||
|
addr1 := sdk.Address([]byte(addr1String))
|
||||||
|
addr2 := sdk.Address([]byte(addr2String))
|
||||||
coins := sdk.Coins{{"atom", 10}}
|
coins := sdk.Coins{{"atom", 10}}
|
||||||
var msg = SendMsg{
|
var msg = SendMsg{
|
||||||
Inputs: []Input{NewInput(addr1, coins)},
|
Inputs: []Input{NewInput(addr1, coins)},
|
||||||
|
@ -188,8 +190,9 @@ func TestSendMsgString(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res := msg.String()
|
res := msg.String()
|
||||||
|
expected := fmt.Sprintf("SendMsg{[Input{%X,10atom}]->[Output{%X,10atom}]}", addr1String, addr2String)
|
||||||
// TODO some failures for bad results
|
// 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) {
|
func TestSendMsgGet(t *testing.T) {
|
||||||
|
@ -275,16 +278,18 @@ func TestIssueMsgValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIssueMsgString(t *testing.T) {
|
func TestIssueMsgString(t *testing.T) {
|
||||||
|
addrString := "loan-from-bank"
|
||||||
|
bankerString := "input"
|
||||||
// Construct a IssueMsg
|
// Construct a IssueMsg
|
||||||
addr := sdk.Address([]byte("loan-from-bank"))
|
addr := sdk.Address([]byte(addrString))
|
||||||
coins := sdk.Coins{{"atom", 10}}
|
coins := sdk.Coins{{"atom", 10}}
|
||||||
var msg = IssueMsg{
|
var msg = IssueMsg{
|
||||||
Banker: sdk.Address([]byte("input")),
|
Banker: sdk.Address([]byte(bankerString)),
|
||||||
Outputs: []Output{NewOutput(addr, coins)},
|
Outputs: []Output{NewOutput(addr, coins)},
|
||||||
}
|
}
|
||||||
res := msg.String()
|
res := msg.String()
|
||||||
// TODO: FIX THIS OUTPUT!
|
expected := fmt.Sprintf("IssueMsg{%X#[Output{%X,10atom}]}", bankerString, addrString)
|
||||||
assert.Equal(t, res, "IssueMsg{696E707574#[Output{36433646363136453244363637323646364432443632363136453642,10atom}]}")
|
assert.Equal(t, expected, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIssueMsgGet(t *testing.T) {
|
func TestIssueMsgGet(t *testing.T) {
|
||||||
|
|
|
@ -7,11 +7,9 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"github.com/tendermint/go-crypto/keys"
|
"github.com/tendermint/go-crypto/keys"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank/commands"
|
"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
|
// SendRequestHandler - http request handler to send coins to a address
|
||||||
func SendRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWriter, *http.Request) {
|
func SendRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWriter, *http.Request) {
|
||||||
c := commands.Commander{cdc}
|
c := commands.Commander{cdc}
|
||||||
|
ctx := context.NewCoreContextFromViper()
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
// collect data
|
// collect data
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
@ -73,9 +72,8 @@ func SendRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWrit
|
||||||
}
|
}
|
||||||
|
|
||||||
// sign
|
// sign
|
||||||
// XXX: OMG
|
ctx = ctx.WithSequence(m.Sequence)
|
||||||
viper.Set(client.FlagSequence, m.Sequence)
|
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
|
||||||
txBytes, err := builder.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
|
@ -83,7 +81,7 @@ func SendRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWrit
|
||||||
}
|
}
|
||||||
|
|
||||||
// send
|
// send
|
||||||
res, err := builder.BroadcastTx(txBytes)
|
res, err := ctx.BroadcastTx(txBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
|
|
|
@ -1,25 +1,157 @@
|
||||||
# IBC CLI Usage
|
# IBC Doubble Hubble
|
||||||
|
|
||||||
## initialize
|
## Remove remaining data
|
||||||
|
|
||||||
```bash
|
```console
|
||||||
basecoind init # copy the recover key
|
> rm -r ~/.chain1
|
||||||
basecli keys add keyname --recover
|
> rm -r ~/.chain2
|
||||||
basecoind start
|
> 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
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
@ -39,8 +39,10 @@ type sendCommander struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c sendCommander) sendIBCTransfer(cmd *cobra.Command, args []string) error {
|
func (c sendCommander) sendIBCTransfer(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := context.NewCoreContextFromViper()
|
||||||
|
|
||||||
// get the from address
|
// get the from address
|
||||||
from, err := builder.GetFromAddress()
|
from, err := ctx.GetFromAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -52,9 +54,7 @@ func (c sendCommander) sendIBCTransfer(cmd *cobra.Command, args []string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// get password
|
// get password
|
||||||
name := viper.GetString(client.FlagName)
|
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, c.cdc)
|
||||||
|
|
||||||
res, err := builder.SignBuildBroadcast(name, msg, c.cdc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ func buildMsg(from sdk.Address) (sdk.Msg, error) {
|
||||||
}
|
}
|
||||||
to := sdk.Address(bz)
|
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))
|
viper.GetString(flagChain))
|
||||||
|
|
||||||
msg := ibc.IBCTransferMsg{
|
msg := ibc.IBCTransferMsg{
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/tendermint/tmlibs/log"
|
||||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
@ -30,6 +31,8 @@ type relayCommander struct {
|
||||||
decoder sdk.AccountDecoder
|
decoder sdk.AccountDecoder
|
||||||
mainStore string
|
mainStore string
|
||||||
ibcStore string
|
ibcStore string
|
||||||
|
|
||||||
|
logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
|
func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
|
||||||
|
@ -38,6 +41,8 @@ func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
|
||||||
decoder: authcmd.GetAccountDecoder(cdc),
|
decoder: authcmd.GetAccountDecoder(cdc),
|
||||||
ibcStore: "ibc",
|
ibcStore: "ibc",
|
||||||
mainStore: "main",
|
mainStore: "main",
|
||||||
|
|
||||||
|
logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
|
@ -68,7 +73,7 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) {
|
||||||
fromChainNode := viper.GetString(FlagFromChainNode)
|
fromChainNode := viper.GetString(FlagFromChainNode)
|
||||||
toChainID := viper.GetString(FlagToChainID)
|
toChainID := viper.GetString(FlagToChainID)
|
||||||
toChainNode := viper.GetString(FlagToChainNode)
|
toChainNode := viper.GetString(FlagToChainNode)
|
||||||
address, err := builder.GetFromAddress()
|
address, err := context.NewCoreContextFromViper().GetFromAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -78,35 +83,34 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) {
|
func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) {
|
||||||
|
ctx := context.NewCoreContextFromViper()
|
||||||
// get password
|
// get password
|
||||||
name := viper.GetString(client.FlagName)
|
passphrase, err := ctx.GetPassphraseFromStdin(ctx.FromAddressName)
|
||||||
passphrase, err := builder.GetPassphraseFromStdin(name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ingressKey := ibc.IngressSequenceKey(fromChainID)
|
ingressKey := ibc.IngressSequenceKey(fromChainID)
|
||||||
|
|
||||||
processedbz, err := query(toChainNode, ingressKey, c.ibcStore)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var processed int64
|
|
||||||
if processedbz == nil {
|
|
||||||
processed = 0
|
|
||||||
} else if err = c.cdc.UnmarshalBinary(processedbz, &processed); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
OUTER:
|
OUTER:
|
||||||
for {
|
for {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
|
processedbz, err := query(toChainNode, ingressKey, c.ibcStore)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var processed int64
|
||||||
|
if processedbz == nil {
|
||||||
|
processed = 0
|
||||||
|
} else if err = c.cdc.UnmarshalBinary(processedbz, &processed); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
lengthKey := ibc.EgressLengthKey(toChainID)
|
lengthKey := ibc.EgressLengthKey(toChainID)
|
||||||
egressLengthbz, err := query(fromChainNode, lengthKey, c.ibcStore)
|
egressLengthbz, err := query(fromChainNode, lengthKey, c.ibcStore)
|
||||||
if err != nil {
|
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
|
continue OUTER
|
||||||
}
|
}
|
||||||
var egressLength int64
|
var egressLength int64
|
||||||
|
@ -115,43 +119,37 @@ OUTER:
|
||||||
} else if err = c.cdc.UnmarshalBinary(egressLengthbz, &egressLength); err != nil {
|
} else if err = c.cdc.UnmarshalBinary(egressLengthbz, &egressLength); err != nil {
|
||||||
panic(err)
|
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++ {
|
for i := processed; i < egressLength; i++ {
|
||||||
egressbz, err := query(fromChainNode, ibc.EgressKey(toChainID, i), c.ibcStore)
|
egressbz, err := query(fromChainNode, ibc.EgressKey(toChainID, i), c.ibcStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error querying egress packet: '%s'\n", err)
|
c.logger.Error("Error querying egress packet", "err", err)
|
||||||
continue OUTER
|
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 {
|
if err != nil {
|
||||||
fmt.Printf("Error broadcasting ingress packet: '%s'\n", err)
|
c.logger.Error("Error broadcasting ingress packet", "err", err)
|
||||||
continue OUTER
|
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) {
|
func query(node string, key []byte, storeName string) (res []byte, err error) {
|
||||||
orig := viper.GetString(client.FlagNode)
|
return context.NewCoreContextFromViper().WithNodeURI(node).Query(key, storeName)
|
||||||
viper.Set(client.FlagNode, node)
|
|
||||||
res, err = builder.Query(key, storeName)
|
|
||||||
viper.Set(client.FlagNode, orig)
|
|
||||||
return res, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c relayCommander) broadcastTx(node string, tx []byte) error {
|
func (c relayCommander) broadcastTx(seq int64, node string, tx []byte) error {
|
||||||
orig := viper.GetString(client.FlagNode)
|
_, err := context.NewCoreContextFromViper().WithNodeURI(node).WithSequence(seq + 1).BroadcastTx(tx)
|
||||||
viper.Set(client.FlagNode, node)
|
|
||||||
seq := c.getSequence(node) + 1
|
|
||||||
viper.Set(client.FlagSequence, seq)
|
|
||||||
_, err := builder.BroadcastTx(tx)
|
|
||||||
viper.Set(client.FlagNode, orig)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +158,7 @@ func (c relayCommander) getSequence(node string) int64 {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
account, err := c.decoder(res)
|
account, err := c.decoder(res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -180,8 +179,8 @@ func (c relayCommander) refine(bz []byte, sequence int64, passphrase string) []b
|
||||||
Sequence: sequence,
|
Sequence: sequence,
|
||||||
}
|
}
|
||||||
|
|
||||||
name := viper.GetString(client.FlagName)
|
ctx := context.NewCoreContextFromViper()
|
||||||
res, err := builder.SignAndBuild(name, passphrase, msg, c.cdc)
|
res, err := ctx.SignAndBuild(ctx.FromAddressName, passphrase, msg, c.cdc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,9 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"github.com/tendermint/go-crypto/keys"
|
"github.com/tendermint/go-crypto/keys"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank/commands"
|
"github.com/cosmos/cosmos-sdk/x/bank/commands"
|
||||||
|
@ -31,6 +29,7 @@ type transferBody struct {
|
||||||
// on a different chain via IBC
|
// on a different chain via IBC
|
||||||
func TransferRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWriter, *http.Request) {
|
func TransferRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWriter, *http.Request) {
|
||||||
c := commands.Commander{cdc}
|
c := commands.Commander{cdc}
|
||||||
|
ctx := context.NewCoreContextFromViper()
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
// collect data
|
// collect data
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
@ -71,9 +70,8 @@ func TransferRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.Response
|
||||||
msg := ibc.IBCTransferMsg{packet}
|
msg := ibc.IBCTransferMsg{packet}
|
||||||
|
|
||||||
// sign
|
// sign
|
||||||
// XXX: OMG
|
ctx = ctx.WithSequence(m.Sequence)
|
||||||
viper.Set(client.FlagSequence, m.Sequence)
|
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
|
||||||
txBytes, err := builder.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
|
@ -81,7 +79,7 @@ func TransferRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.Response
|
||||||
}
|
}
|
||||||
|
|
||||||
// send
|
// send
|
||||||
res, err := builder.BroadcastTx(txBytes)
|
res, err := ctx.BroadcastTx(txBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
|
|
|
@ -9,8 +9,7 @@ import (
|
||||||
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/simplestake"
|
"github.com/cosmos/cosmos-sdk/x/simplestake"
|
||||||
|
@ -48,7 +47,9 @@ type commander struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co commander) bondTxCmd(cmd *cobra.Command, args []string) error {
|
func (co commander) bondTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
from, err := builder.GetFromAddress()
|
ctx := context.NewCoreContextFromViper()
|
||||||
|
|
||||||
|
from, err := ctx.GetFromAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -82,7 +83,7 @@ func (co commander) bondTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co commander) unbondTxCmd(cmd *cobra.Command, args []string) error {
|
func (co commander) unbondTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
from, err := builder.GetFromAddress()
|
from, err := context.NewCoreContextFromViper().GetFromAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -93,8 +94,8 @@ func (co commander) unbondTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co commander) sendMsg(msg sdk.Msg) error {
|
func (co commander) sendMsg(msg sdk.Msg) error {
|
||||||
name := viper.GetString(client.FlagName)
|
ctx := context.NewCoreContextFromViper()
|
||||||
res, err := builder.SignBuildBroadcast(name, msg, co.cdc)
|
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, co.cdc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
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"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire" // XXX fix
|
"github.com/cosmos/cosmos-sdk/wire" // XXX fix
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
"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)
|
key := PrefixedKey(stake.MsgType, stake.CandidatesKey)
|
||||||
|
|
||||||
res, err := builder.Query(key, storeName)
|
ctx := context.NewCoreContextFromViper()
|
||||||
|
res, err := ctx.Query(key, storeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -87,7 +88,9 @@ func GetCmdQueryCandidate(cdc *wire.Codec, storeName string) *cobra.Command {
|
||||||
|
|
||||||
key := PrefixedKey(stake.MsgType, stake.GetCandidateKey(addr))
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -133,7 +136,9 @@ func GetCmdQueryDelegatorBond(cdc *wire.Codec, storeName string) *cobra.Command
|
||||||
|
|
||||||
key := PrefixedKey(stake.MsgType, stake.GetDelegatorBondKey(delegator, addr, cdc))
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -175,7 +180,9 @@ func GetCmdQueryDelegatorBonds(cdc *wire.Codec, storeName string) *cobra.Command
|
||||||
|
|
||||||
key := PrefixedKey(stake.MsgType, stake.GetDelegatorBondsKey(delegator, cdc))
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"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"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
"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)
|
msg := stake.NewMsgDeclareCandidacy(candidateAddr, pk, amount, description)
|
||||||
|
|
||||||
// build and sign the transaction, then broadcast to Tendermint
|
// build and sign the transaction, then broadcast to Tendermint
|
||||||
name := viper.GetString(client.FlagName)
|
ctx := context.NewCoreContextFromViper()
|
||||||
res, err := builder.SignBuildBroadcast(name, msg, cdc)
|
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -129,8 +129,8 @@ func GetCmdEditCandidacy(cdc *wire.Codec) *cobra.Command {
|
||||||
msg := stake.NewMsgEditCandidacy(candidateAddr, description)
|
msg := stake.NewMsgEditCandidacy(candidateAddr, description)
|
||||||
|
|
||||||
// build and sign the transaction, then broadcast to Tendermint
|
// build and sign the transaction, then broadcast to Tendermint
|
||||||
name := viper.GetString(client.FlagName)
|
ctx := context.NewCoreContextFromViper()
|
||||||
res, err := builder.SignBuildBroadcast(name, msg, cdc)
|
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -165,8 +165,8 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
|
||||||
msg := stake.NewMsgDelegate(delegatorAddr, candidateAddr, amount)
|
msg := stake.NewMsgDelegate(delegatorAddr, candidateAddr, amount)
|
||||||
|
|
||||||
// build and sign the transaction, then broadcast to Tendermint
|
// build and sign the transaction, then broadcast to Tendermint
|
||||||
name := viper.GetString(client.FlagName)
|
ctx := context.NewCoreContextFromViper()
|
||||||
res, err := builder.SignBuildBroadcast(name, msg, cdc)
|
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -212,8 +212,8 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
|
||||||
msg := stake.NewMsgUnbond(delegatorAddr, candidateAddr, sharesStr)
|
msg := stake.NewMsgUnbond(delegatorAddr, candidateAddr, sharesStr)
|
||||||
|
|
||||||
// build and sign the transaction, then broadcast to Tendermint
|
// build and sign the transaction, then broadcast to Tendermint
|
||||||
name := viper.GetString(client.FlagName)
|
ctx := context.NewCoreContextFromViper()
|
||||||
res, err := builder.SignBuildBroadcast(name, msg, cdc)
|
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,9 @@ func ErrBadDelegatorAddr() sdk.Error {
|
||||||
func ErrCandidateExistsAddr() sdk.Error {
|
func ErrCandidateExistsAddr() sdk.Error {
|
||||||
return newError(CodeInvalidValidator, "Candidate already exist, cannot re-declare candidacy")
|
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 {
|
func ErrMissingSignature() sdk.Error {
|
||||||
return newError(CodeInvalidValidator, "Missing signature")
|
return newError(CodeInvalidValidator, "Missing signature")
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint
|
//nolint
|
||||||
|
@ -15,40 +16,6 @@ const (
|
||||||
GasUnbond int64 = 20
|
GasUnbond int64 = 20
|
||||||
)
|
)
|
||||||
|
|
||||||
//XXX fix initstater
|
|
||||||
// separated for testing
|
|
||||||
//func InitState(ctx sdk.Context, k Keeper, key, value string) sdk.Error {
|
|
||||||
|
|
||||||
//params := k.GetParams(ctx)
|
|
||||||
//switch key {
|
|
||||||
//case "allowed_bond_denom":
|
|
||||||
//params.BondDenom = value
|
|
||||||
//case "max_vals", "gas_bond", "gas_unbond":
|
|
||||||
|
|
||||||
//i, err := strconv.Atoi(value)
|
|
||||||
//if err != nil {
|
|
||||||
//return sdk.ErrUnknownRequest(fmt.Sprintf("input must be integer, Error: %v", err.Error()))
|
|
||||||
//}
|
|
||||||
|
|
||||||
//switch key {
|
|
||||||
//case "max_vals":
|
|
||||||
//if i < 0 {
|
|
||||||
//return sdk.ErrUnknownRequest("cannot designate negative max validators")
|
|
||||||
//}
|
|
||||||
//params.MaxValidators = uint16(i)
|
|
||||||
//case "gas_bond":
|
|
||||||
//GasDelegate = int64(i)
|
|
||||||
//case "gas_unbound":
|
|
||||||
//GasUnbond = int64(i)
|
|
||||||
//}
|
|
||||||
//default:
|
|
||||||
//return sdk.ErrUnknownRequest(key)
|
|
||||||
//}
|
|
||||||
|
|
||||||
//k.setParams(params)
|
|
||||||
//return nil
|
|
||||||
//}
|
|
||||||
|
|
||||||
//_______________________________________________________________________
|
//_______________________________________________________________________
|
||||||
|
|
||||||
func NewHandler(k Keeper, ck bank.CoinKeeper) sdk.Handler {
|
func NewHandler(k Keeper, ck bank.CoinKeeper) sdk.Handler {
|
||||||
|
@ -69,16 +36,16 @@ func NewHandler(k Keeper, ck bank.CoinKeeper) sdk.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//_____________________________________________________________________
|
//_______________________________________________
|
||||||
|
|
||||||
// XXX should be send in the msg (init in CLI)
|
// NewEndBlocker generates sdk.EndBlocker
|
||||||
//func getSender() sdk.Address {
|
// Performs tick functionality
|
||||||
//signers := msg.GetSigners()
|
func NewEndBlocker(k Keeper) sdk.EndBlocker {
|
||||||
//if len(signers) != 1 {
|
return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
|
||||||
//return sdk.ErrUnauthorized("there can only be one signer for staking transaction").Result()
|
res.ValidatorUpdates = k.Tick(ctx)
|
||||||
//}
|
return
|
||||||
//sender := signers[0]
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
//_____________________________________________________________________
|
//_____________________________________________________________________
|
||||||
|
|
||||||
|
@ -106,7 +73,11 @@ func handleMsgDeclareCandidacy(ctx sdk.Context, msg MsgDeclareCandidacy, k Keepe
|
||||||
|
|
||||||
// move coins from the msg.Address account to a (self-bond) delegator account
|
// move coins from the msg.Address account to a (self-bond) delegator account
|
||||||
// the candidate account and global shares are updated within here
|
// the candidate account and global shares are updated within here
|
||||||
return delegateWithCandidate(ctx, k, msg.CandidateAddr, msg.Bond, candidate).Result()
|
err := delegate(ctx, k, msg.CandidateAddr, msg.Bond, candidate)
|
||||||
|
if err != nil {
|
||||||
|
return err.Result()
|
||||||
|
}
|
||||||
|
return sdk.Result{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk.Result {
|
func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk.Result {
|
||||||
|
@ -145,25 +116,29 @@ func handleMsgDelegate(ctx sdk.Context, msg MsgDelegate, k Keeper) sdk.Result {
|
||||||
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
|
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
|
||||||
return ErrBadBondingDenom().Result()
|
return ErrBadBondingDenom().Result()
|
||||||
}
|
}
|
||||||
|
if candidate.Status == Revoked {
|
||||||
|
return ErrCandidateRevoked().Result()
|
||||||
|
}
|
||||||
if ctx.IsCheckTx() {
|
if ctx.IsCheckTx() {
|
||||||
return sdk.Result{
|
return sdk.Result{
|
||||||
GasUsed: GasDelegate,
|
GasUsed: GasDelegate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return delegateWithCandidate(ctx, k, msg.DelegatorAddr, msg.Bond, candidate).Result()
|
err := delegate(ctx, k, msg.DelegatorAddr, msg.Bond, candidate)
|
||||||
|
if err != nil {
|
||||||
|
return err.Result()
|
||||||
|
}
|
||||||
|
return sdk.Result{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func delegateWithCandidate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
|
// common functionality between handlers
|
||||||
|
func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
|
||||||
bondAmt sdk.Coin, candidate Candidate) sdk.Error {
|
bondAmt sdk.Coin, candidate Candidate) sdk.Error {
|
||||||
|
|
||||||
if candidate.Status == Revoked { //candidate has been withdrawn
|
|
||||||
return ErrBondNotNominated()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get or create the delegator bond
|
// Get or create the delegator bond
|
||||||
existingBond, found := k.getDelegatorBond(ctx, delegatorAddr, candidate.Address)
|
bond, found := k.getDelegatorBond(ctx, delegatorAddr, candidate.Address)
|
||||||
if !found {
|
if !found {
|
||||||
existingBond = DelegatorBond{
|
bond = DelegatorBond{
|
||||||
DelegatorAddr: delegatorAddr,
|
DelegatorAddr: delegatorAddr,
|
||||||
CandidateAddr: candidate.Address,
|
CandidateAddr: candidate.Address,
|
||||||
Shares: sdk.ZeroRat,
|
Shares: sdk.ZeroRat,
|
||||||
|
@ -171,25 +146,17 @@ func delegateWithCandidate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account new shares, save
|
// Account new shares, save
|
||||||
err := BondCoins(ctx, k, existingBond, candidate, bondAmt)
|
pool := k.GetPool(ctx)
|
||||||
|
_, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{bondAmt})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
k.setDelegatorBond(ctx, existingBond)
|
pool, candidate, newShares := pool.candidateAddTokens(candidate, bondAmt.Amount)
|
||||||
k.setCandidate(ctx, candidate)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform all the actions required to bond tokens to a delegator bond from their account
|
|
||||||
func BondCoins(ctx sdk.Context, k Keeper, bond DelegatorBond, candidate Candidate, amount sdk.Coin) sdk.Error {
|
|
||||||
|
|
||||||
_, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{amount})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
newShares := k.candidateAddTokens(ctx, candidate, amount.Amount)
|
|
||||||
bond.Shares = bond.Shares.Add(newShares)
|
bond.Shares = bond.Shares.Add(newShares)
|
||||||
|
|
||||||
k.setDelegatorBond(ctx, bond)
|
k.setDelegatorBond(ctx, bond)
|
||||||
|
k.setCandidate(ctx, candidate)
|
||||||
|
k.setPool(ctx, pool)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +183,7 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
||||||
return ErrNotEnoughBondShares(msg.Shares).Result()
|
return ErrNotEnoughBondShares(msg.Shares).Result()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !bond.Shares.GT(shares) {
|
if bond.Shares.LT(shares) {
|
||||||
return ErrNotEnoughBondShares(msg.Shares).Result()
|
return ErrNotEnoughBondShares(msg.Shares).Result()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,16 +225,19 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the coins
|
// Add the coins
|
||||||
returnAmount := k.candidateRemoveShares(ctx, candidate, shares)
|
p := k.GetPool(ctx)
|
||||||
|
p, candidate, returnAmount := p.candidateRemoveShares(candidate, shares)
|
||||||
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
|
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
|
||||||
k.coinKeeper.AddCoins(ctx, bond.DelegatorAddr, returnCoins)
|
k.coinKeeper.AddCoins(ctx, bond.DelegatorAddr, returnCoins)
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
|
||||||
// revoke candidate if necessary
|
// revoke candidate if necessary
|
||||||
if revokeCandidacy {
|
if revokeCandidacy {
|
||||||
|
|
||||||
// change the share types to unbonded if they were not already
|
// change the share types to unbonded if they were not already
|
||||||
if candidate.Status == Bonded {
|
if candidate.Status == Bonded {
|
||||||
k.bondedToUnbondedPool(ctx, candidate)
|
p, candidate = p.bondedToUnbondedPool(candidate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lastly update the status
|
// lastly update the status
|
||||||
|
@ -280,25 +250,43 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
||||||
} else {
|
} else {
|
||||||
k.setCandidate(ctx, candidate)
|
k.setCandidate(ctx, candidate)
|
||||||
}
|
}
|
||||||
|
k.setPool(ctx, p)
|
||||||
return sdk.Result{}
|
return sdk.Result{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX where this used
|
// TODO use or remove
|
||||||
// Perform all the actions required to bond tokens to a delegator bond from their account
|
//// Perform all the actions required to bond tokens to a delegator bond from their account
|
||||||
func UnbondCoins(ctx sdk.Context, k Keeper, bond DelegatorBond, candidate Candidate, shares sdk.Rat) sdk.Error {
|
//func BondCoins(ctx sdk.Context, k Keeper, bond DelegatorBond,
|
||||||
|
//candidate Candidate, amount sdk.Coin) (DelegatorBond, Candidate, Pool, sdk.Error) {
|
||||||
|
|
||||||
// subtract bond tokens from delegator bond
|
//pool := k.GetPool(ctx)
|
||||||
if bond.Shares.LT(shares) {
|
//_, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{amount})
|
||||||
return sdk.ErrInsufficientFunds("") //XXX variables inside
|
//if err != nil {
|
||||||
}
|
//return bond, candidate, pool, err
|
||||||
bond.Shares = bond.Shares.Sub(shares)
|
//}
|
||||||
|
//pool, candidate, newShares := pool.candidateAddTokens(candidate, amount.Amount)
|
||||||
|
//bond.Shares = bond.Shares.Add(newShares)
|
||||||
|
//return bond, candidate, pool, nil
|
||||||
|
//}
|
||||||
|
//// Perform all the actions required to bond tokens to a delegator bond from their account
|
||||||
|
//func UnbondCoins(ctx sdk.Context, k Keeper, bond DelegatorBond,
|
||||||
|
//candidate Candidate, shares sdk.Rat) (DelegatorBond, Candidate, Pool, sdk.Error) {
|
||||||
|
|
||||||
returnAmount := k.candidateRemoveShares(ctx, candidate, shares)
|
//pool := k.GetPool(ctx)
|
||||||
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
|
|
||||||
|
|
||||||
_, err := k.coinKeeper.AddCoins(ctx, candidate.Address, returnCoins)
|
//// subtract bond tokens from delegator bond
|
||||||
if err != nil {
|
//if bond.Shares.LT(shares) {
|
||||||
return err
|
//errMsg := fmt.Sprintf("cannot unbond %v shares, only have %v shares available", shares, bond.Shares)
|
||||||
}
|
//return bond, candidate, pool, sdk.ErrInsufficientFunds(errMsg)
|
||||||
return nil
|
//}
|
||||||
}
|
//bond.Shares = bond.Shares.Sub(shares)
|
||||||
|
|
||||||
|
//pool, candidate, returnAmount := p.candidateRemoveShares(candidate, shares)
|
||||||
|
//returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
|
||||||
|
|
||||||
|
//_, err := k.coinKeeper.AddCoins(ctx, candidate.Address, returnCoins)
|
||||||
|
//if err != nil {
|
||||||
|
//return err
|
||||||
|
//}
|
||||||
|
//return bond, candidate, pool, nil
|
||||||
|
//}
|
||||||
|
|
|
@ -1,248 +1,308 @@
|
||||||
package stake
|
package stake
|
||||||
|
|
||||||
//import (
|
import (
|
||||||
//"strconv"
|
"strconv"
|
||||||
//"testing"
|
"testing"
|
||||||
|
|
||||||
//"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
//"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
//crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
|
||||||
//sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
//)
|
)
|
||||||
|
|
||||||
////______________________________________________________________________
|
//______________________________________________________________________
|
||||||
|
|
||||||
//func newTestMsgDeclareCandidacy(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgDeclareCandidacy {
|
func newTestMsgDeclareCandidacy(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgDeclareCandidacy {
|
||||||
//return MsgDeclareCandidacy{
|
return MsgDeclareCandidacy{
|
||||||
//Description: Description{},
|
Description: Description{},
|
||||||
//CandidateAddr: address,
|
CandidateAddr: address,
|
||||||
//Bond: sdk.Coin{"fermion", amt},
|
Bond: sdk.Coin{"fermion", amt},
|
||||||
//PubKey: pubKey,
|
PubKey: pubKey,
|
||||||
//}
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
//func newTestMsgDelegate(amt int64, delegatorAddr, candidateAddr sdk.Address) MsgDelegate {
|
func newTestMsgDelegate(delegatorAddr, candidateAddr sdk.Address, amt int64) MsgDelegate {
|
||||||
//return MsgDelegate{
|
return MsgDelegate{
|
||||||
//DelegatorAddr: delegatorAddr,
|
DelegatorAddr: delegatorAddr,
|
||||||
//CandidateAddr: candidateAddr,
|
CandidateAddr: candidateAddr,
|
||||||
//Bond: sdk.Coin{"fermion", amt},
|
Bond: sdk.Coin{"fermion", amt},
|
||||||
//}
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
//func TestDuplicatesMsgDeclareCandidacy(t *testing.T) {
|
//______________________________________________________________________
|
||||||
//ctxDeliver, _, keeper := createTestInput(t, addrs[0], false, 1000)
|
|
||||||
//ctxCheck, _, keeper := createTestInput(t, addrs[0], true, 1000)
|
|
||||||
|
|
||||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], 10)
|
func TestDuplicatesMsgDeclareCandidacy(t *testing.T) {
|
||||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
ctx, _, keeper := createTestInput(t, false, 1000)
|
||||||
//assert.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
|
||||||
|
|
||||||
//// one sender can bond to two different addresses
|
candidateAddr := addrs[0]
|
||||||
//msgDeclareCandidacy.Address = addrs[1]
|
pk := pks[0]
|
||||||
//err := checker.declareCandidacy(msgDeclareCandidacy)
|
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pk, 10)
|
||||||
//assert.Nil(t, err, "didn't expected error on checkTx")
|
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||||
|
assert.True(t, got.IsOK(), "%v", got)
|
||||||
|
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||||
|
require.True(t, found)
|
||||||
|
assert.Equal(t, Unbonded, candidate.Status)
|
||||||
|
assert.Equal(t, candidateAddr, candidate.Address)
|
||||||
|
assert.Equal(t, pk, candidate.PubKey)
|
||||||
|
assert.Equal(t, sdk.NewRat(10), candidate.Assets)
|
||||||
|
assert.Equal(t, sdk.NewRat(10), candidate.Liabilities)
|
||||||
|
assert.Equal(t, Description{}, candidate.Description)
|
||||||
|
|
||||||
//// two addrs cant bond to the same pubkey
|
// one candidate cannot bond twice
|
||||||
//checker.sender = addrs[1]
|
msgDeclareCandidacy.PubKey = pks[1]
|
||||||
//msgDeclareCandidacy.Address = addrs[0]
|
got = handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||||
//err = checker.declareCandidacy(msgDeclareCandidacy)
|
assert.False(t, got.IsOK(), "%v", got)
|
||||||
//assert.NotNil(t, err, "expected error on checkTx")
|
}
|
||||||
//}
|
|
||||||
|
|
||||||
//func TestIncrementsMsgDelegate(t *testing.T) {
|
func TestIncrementsMsgDelegate(t *testing.T) {
|
||||||
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 1000)
|
initBond := int64(1000)
|
||||||
|
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||||
|
params := keeper.GetParams(ctx)
|
||||||
|
|
||||||
//// first declare candidacy
|
bondAmount := int64(10)
|
||||||
//bondAmount := int64(10)
|
candidateAddr, delegatorAddr := addrs[0], addrs[1]
|
||||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], bondAmount)
|
|
||||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
|
||||||
//assert.NoError(t, got, "expected declare candidacy msg to be ok, got %v", got)
|
|
||||||
//expectedBond := bondAmount // 1 since we send 1 at the start of loop,
|
|
||||||
|
|
||||||
//// just send the same msgbond multiple times
|
// first declare candidacy
|
||||||
//msgDelegate := newTestMsgDelegate(bondAmount, addrs[0])
|
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[0], bondAmount)
|
||||||
//for i := 0; i < 5; i++ {
|
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||||
//got := deliverer.delegate(msgDelegate)
|
assert.True(t, got.IsOK(), "expected declare candidacy msg to be ok, got %v", got)
|
||||||
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
|
||||||
|
|
||||||
////Check that the accounts and the bond account have the appropriate values
|
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||||
//candidates := mapper.GetCandidates()
|
require.True(t, found)
|
||||||
//expectedBond += bondAmount
|
assert.Equal(t, bondAmount, candidate.Liabilities.Evaluate())
|
||||||
////expectedSender := initSender - expectedBond
|
assert.Equal(t, bondAmount, candidate.Assets.Evaluate())
|
||||||
//gotBonded := candidates[0].Liabilities.Evaluate()
|
|
||||||
////gotSender := accStore[string(deliverer.sender)] //XXX use StoreMapper
|
|
||||||
//assert.Equal(t, expectedBond, gotBonded, "i: %v, %v, %v", i, expectedBond, gotBonded)
|
|
||||||
////assert.Equal(t, expectedSender, gotSender, "i: %v, %v, %v", i, expectedSender, gotSender) // XXX fix
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
//func TestIncrementsMsgUnbond(t *testing.T) {
|
// just send the same msgbond multiple times
|
||||||
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 0)
|
msgDelegate := newTestMsgDelegate(delegatorAddr, candidateAddr, bondAmount)
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
got := handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||||
|
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||||
|
|
||||||
//// set initial bond
|
//Check that the accounts and the bond account have the appropriate values
|
||||||
//initBond := int64(1000)
|
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||||
////accStore[string(deliverer.sender)] = initBond //XXX use StoreMapper
|
require.True(t, found)
|
||||||
//got := deliverer.declareCandidacy(newTestMsgDeclareCandidacy(addrs[0], pks[0], initBond))
|
bond, found := keeper.getDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||||
//assert.NoError(t, got, "expected initial bond msg to be ok, got %v", got)
|
require.True(t, found)
|
||||||
|
|
||||||
//// just send the same msgunbond multiple times
|
expBond := int64(i+1) * bondAmount
|
||||||
//// XXX use decimals here
|
expLiabilities := int64(i+2) * bondAmount // (1 self delegation)
|
||||||
//unbondShares, unbondSharesStr := int64(10), "10"
|
expDelegatorAcc := initBond - expBond
|
||||||
//msgUndelegate := NewMsgUnbond(addrs[0], unbondSharesStr)
|
|
||||||
//nUnbonds := 5
|
|
||||||
//for i := 0; i < nUnbonds; i++ {
|
|
||||||
//got := deliverer.unbond(msgUndelegate)
|
|
||||||
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
|
||||||
|
|
||||||
////Check that the accounts and the bond account have the appropriate values
|
gotBond := bond.Shares.Evaluate()
|
||||||
//candidates := mapper.GetCandidates()
|
gotLiabilities := candidate.Liabilities.Evaluate()
|
||||||
//expectedBond := initBond - int64(i+1)*unbondShares // +1 since we send 1 at the start of loop
|
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
|
||||||
////expectedSender := initSender + (initBond - expectedBond)
|
|
||||||
//gotBonded := candidates[0].Liabilities.Evaluate()
|
|
||||||
////gotSender := accStore[string(deliverer.sender)] // XXX use storemapper
|
|
||||||
|
|
||||||
//assert.Equal(t, expectedBond, gotBonded, "%v, %v", expectedBond, gotBonded)
|
require.Equal(t, expBond, gotBond,
|
||||||
////assert.Equal(t, expectedSender, gotSender, "%v, %v", expectedSender, gotSender) //XXX fix
|
"i: %v\nexpBond: %v\ngotBond: %v\ncandidate: %v\nbond: %v\n",
|
||||||
//}
|
i, expBond, gotBond, candidate, bond)
|
||||||
|
require.Equal(t, expLiabilities, gotLiabilities,
|
||||||
|
"i: %v\nexpLiabilities: %v\ngotLiabilities: %v\ncandidate: %v\nbond: %v\n",
|
||||||
|
i, expLiabilities, gotLiabilities, candidate, bond)
|
||||||
|
require.Equal(t, expDelegatorAcc, gotDelegatorAcc,
|
||||||
|
"i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\ncandidate: %v\nbond: %v\n",
|
||||||
|
i, expDelegatorAcc, gotDelegatorAcc, candidate, bond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//// these are more than we have bonded now
|
func TestIncrementsMsgUnbond(t *testing.T) {
|
||||||
//errorCases := []int64{
|
initBond := int64(1000)
|
||||||
////1<<64 - 1, // more than int64
|
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||||
////1<<63 + 1, // more than int64
|
params := keeper.GetParams(ctx)
|
||||||
//1<<63 - 1,
|
|
||||||
//1 << 31,
|
|
||||||
//initBond,
|
|
||||||
//}
|
|
||||||
//for _, c := range errorCases {
|
|
||||||
//unbondShares := strconv.Itoa(int(c))
|
|
||||||
//msgUndelegate := NewMsgUnbond(addrs[0], unbondShares)
|
|
||||||
//got = deliverer.unbond(msgUndelegate)
|
|
||||||
//assert.Error(t, got, "expected unbond msg to fail")
|
|
||||||
//}
|
|
||||||
|
|
||||||
//leftBonded := initBond - unbondShares*int64(nUnbonds)
|
// declare candidacy, delegate
|
||||||
|
candidateAddr, delegatorAddr := addrs[0], addrs[1]
|
||||||
|
|
||||||
//// should be unable to unbond one more than we have
|
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[0], initBond)
|
||||||
//msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)+1))
|
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||||
//got = deliverer.unbond(msgUndelegate)
|
assert.True(t, got.IsOK(), "expected declare-candidacy to be ok, got %v", got)
|
||||||
//assert.Error(t, got, "expected unbond msg to fail")
|
|
||||||
|
|
||||||
//// should be able to unbond just what we have
|
msgDelegate := newTestMsgDelegate(delegatorAddr, candidateAddr, initBond)
|
||||||
//msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)))
|
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||||
//got = deliverer.unbond(msgUndelegate)
|
assert.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
|
||||||
//assert.NoError(t, got, "expected unbond msg to pass")
|
|
||||||
//}
|
|
||||||
|
|
||||||
//func TestMultipleMsgDeclareCandidacy(t *testing.T) {
|
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||||
//initSender := int64(1000)
|
require.True(t, found)
|
||||||
//ctx, accStore, mapper, deliverer := createTestInput(t, addrs[0], false, initSender)
|
assert.Equal(t, initBond*2, candidate.Liabilities.Evaluate())
|
||||||
//addrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
|
assert.Equal(t, initBond*2, candidate.Assets.Evaluate())
|
||||||
|
|
||||||
//// bond them all
|
// just send the same msgUnbond multiple times
|
||||||
//for i, addr := range addrs {
|
// TODO use decimals here
|
||||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[i], pks[i], 10)
|
unbondShares, unbondSharesStr := int64(10), "10"
|
||||||
//deliverer.sender = addr
|
msgUnbond := NewMsgUnbond(delegatorAddr, candidateAddr, unbondSharesStr)
|
||||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
numUnbonds := 5
|
||||||
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
for i := 0; i < numUnbonds; i++ {
|
||||||
|
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||||
|
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||||
|
|
||||||
////Check that the account is bonded
|
//Check that the accounts and the bond account have the appropriate values
|
||||||
//candidates := mapper.GetCandidates()
|
candidate, found = keeper.GetCandidate(ctx, candidateAddr)
|
||||||
//require.Equal(t, i, len(candidates))
|
require.True(t, found)
|
||||||
//val := candidates[i]
|
bond, found := keeper.getDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||||
//balanceExpd := initSender - 10
|
require.True(t, found)
|
||||||
//balanceGot := accStore.GetAccount(ctx, val.Address).GetCoins()
|
|
||||||
//assert.Equal(t, i+1, len(candidates), "expected %d candidates got %d, candidates: %v", i+1, len(candidates), candidates)
|
|
||||||
//assert.Equal(t, 10, int(val.Liabilities.Evaluate()), "expected %d shares, got %d", 10, val.Liabilities)
|
|
||||||
//assert.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// unbond them all
|
expBond := initBond - int64(i+1)*unbondShares
|
||||||
//for i, addr := range addrs {
|
expLiabilities := 2*initBond - int64(i+1)*unbondShares
|
||||||
//candidatePre := mapper.GetCandidate(addrs[i])
|
expDelegatorAcc := initBond - expBond
|
||||||
//msgUndelegate := NewMsgUnbond(addrs[i], "10")
|
|
||||||
//deliverer.sender = addr
|
|
||||||
//got := deliverer.unbond(msgUndelegate)
|
|
||||||
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
|
||||||
|
|
||||||
////Check that the account is unbonded
|
gotBond := bond.Shares.Evaluate()
|
||||||
//candidates := mapper.GetCandidates()
|
gotLiabilities := candidate.Liabilities.Evaluate()
|
||||||
//assert.Equal(t, len(addrs)-(i+1), len(candidates), "expected %d candidates got %d", len(addrs)-(i+1), len(candidates))
|
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
|
||||||
|
|
||||||
//candidatePost := mapper.GetCandidate(addrs[i])
|
require.Equal(t, expBond, gotBond,
|
||||||
//balanceExpd := initSender
|
"i: %v\nexpBond: %v\ngotBond: %v\ncandidate: %v\nbond: %v\n",
|
||||||
//balanceGot := accStore.GetAccount(ctx, candidatePre.Address).GetCoins()
|
i, expBond, gotBond, candidate, bond)
|
||||||
//assert.Nil(t, candidatePost, "expected nil candidate retrieve, got %d", 0, candidatePost)
|
require.Equal(t, expLiabilities, gotLiabilities,
|
||||||
//assert.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
|
"i: %v\nexpLiabilities: %v\ngotLiabilities: %v\ncandidate: %v\nbond: %v\n",
|
||||||
//}
|
i, expLiabilities, gotLiabilities, candidate, bond)
|
||||||
//}
|
require.Equal(t, expDelegatorAcc, gotDelegatorAcc,
|
||||||
|
"i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\ncandidate: %v\nbond: %v\n",
|
||||||
|
i, expDelegatorAcc, gotDelegatorAcc, candidate, bond)
|
||||||
|
}
|
||||||
|
|
||||||
//func TestMultipleMsgDelegate(t *testing.T) {
|
// these are more than we have bonded now
|
||||||
//sender, delegators := addrs[0], addrs[1:]
|
errorCases := []int64{
|
||||||
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 1000)
|
//1<<64 - 1, // more than int64
|
||||||
|
//1<<63 + 1, // more than int64
|
||||||
|
1<<63 - 1,
|
||||||
|
1 << 31,
|
||||||
|
initBond,
|
||||||
|
}
|
||||||
|
for _, c := range errorCases {
|
||||||
|
unbondShares := strconv.Itoa(int(c))
|
||||||
|
msgUnbond := NewMsgUnbond(delegatorAddr, candidateAddr, unbondShares)
|
||||||
|
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||||
|
require.False(t, got.IsOK(), "expected unbond msg to fail")
|
||||||
|
}
|
||||||
|
|
||||||
////first make a candidate
|
leftBonded := initBond - unbondShares*int64(numUnbonds)
|
||||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(sender, pks[0], 10)
|
|
||||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
|
||||||
//require.NoError(t, got, "expected msg to be ok, got %v", got)
|
|
||||||
|
|
||||||
//// delegate multiple parties
|
// should be unable to unbond one more than we have
|
||||||
//for i, delegator := range delegators {
|
unbondSharesStr = strconv.Itoa(int(leftBonded) + 1)
|
||||||
//msgDelegate := newTestMsgDelegate(10, sender)
|
msgUnbond = NewMsgUnbond(delegatorAddr, candidateAddr, unbondSharesStr)
|
||||||
//deliverer.sender = delegator
|
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||||
//got := deliverer.delegate(msgDelegate)
|
assert.False(t, got.IsOK(),
|
||||||
//require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
|
||||||
|
|
||||||
////Check that the account is bonded
|
// should be able to unbond just what we have
|
||||||
//bond := mapper.getDelegatorBond(delegator, sender)
|
unbondSharesStr = strconv.Itoa(int(leftBonded))
|
||||||
//assert.NotNil(t, bond, "expected delegatee bond %d to exist", bond)
|
msgUnbond = NewMsgUnbond(delegatorAddr, candidateAddr, unbondSharesStr)
|
||||||
//}
|
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||||
|
assert.True(t, got.IsOK(),
|
||||||
|
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
|
||||||
|
}
|
||||||
|
|
||||||
//// unbond them all
|
func TestMultipleMsgDeclareCandidacy(t *testing.T) {
|
||||||
//for i, delegator := range delegators {
|
initBond := int64(1000)
|
||||||
//msgUndelegate := NewMsgUnbond(sender, "10")
|
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||||
//deliverer.sender = delegator
|
params := keeper.GetParams(ctx)
|
||||||
//got := deliverer.unbond(msgUndelegate)
|
candidateAddrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
|
||||||
//require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
|
||||||
|
|
||||||
////Check that the account is unbonded
|
// bond them all
|
||||||
//bond := mapper.getDelegatorBond(delegator, sender)
|
for i, candidateAddr := range candidateAddrs {
|
||||||
//assert.Nil(t, bond, "expected delegatee bond %d to be nil", bond)
|
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[i], 10)
|
||||||
//}
|
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||||
//}
|
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||||
|
|
||||||
//func TestVoidCandidacy(t *testing.T) {
|
//Check that the account is bonded
|
||||||
//sender, delegator := addrs[0], addrs[1]
|
candidates := keeper.GetCandidates(ctx, 100)
|
||||||
//_, _, _, deliverer := createTestInput(t, addrs[0], false, 1000)
|
require.Equal(t, (i + 1), len(candidates))
|
||||||
|
val := candidates[i]
|
||||||
|
balanceExpd := initBond - 10
|
||||||
|
balanceGot := accMapper.GetAccount(ctx, val.Address).GetCoins().AmountOf(params.BondDenom)
|
||||||
|
require.Equal(t, i+1, len(candidates), "expected %d candidates got %d, candidates: %v", i+1, len(candidates), candidates)
|
||||||
|
require.Equal(t, 10, int(val.Liabilities.Evaluate()), "expected %d shares, got %d", 10, val.Liabilities)
|
||||||
|
require.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
|
||||||
|
}
|
||||||
|
|
||||||
//// create the candidate
|
// unbond them all
|
||||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], 10)
|
for i, candidateAddr := range candidateAddrs {
|
||||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
candidatePre, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||||
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
require.True(t, found)
|
||||||
|
msgUnbond := NewMsgUnbond(candidateAddr, candidateAddr, "10") // self-delegation
|
||||||
|
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||||
|
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||||
|
|
||||||
//// bond a delegator
|
//Check that the account is unbonded
|
||||||
//msgDelegate := newTestMsgDelegate(10, addrs[0])
|
candidates := keeper.GetCandidates(ctx, 100)
|
||||||
//deliverer.sender = delegator
|
require.Equal(t, len(candidateAddrs)-(i+1), len(candidates),
|
||||||
//got = deliverer.delegate(msgDelegate)
|
"expected %d candidates got %d", len(candidateAddrs)-(i+1), len(candidates))
|
||||||
//require.NoError(t, got, "expected ok, got %v", got)
|
|
||||||
|
|
||||||
//// unbond the candidates bond portion
|
_, found = keeper.GetCandidate(ctx, candidateAddr)
|
||||||
//msgUndelegate := NewMsgUnbond(addrs[0], "10")
|
require.False(t, found)
|
||||||
//deliverer.sender = sender
|
|
||||||
//got = deliverer.unbond(msgUndelegate)
|
|
||||||
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
|
||||||
|
|
||||||
//// test that this pubkey cannot yet be bonded too
|
expBalance := initBond
|
||||||
//deliverer.sender = delegator
|
gotBalance := accMapper.GetAccount(ctx, candidatePre.Address).GetCoins().AmountOf(params.BondDenom)
|
||||||
//got = deliverer.delegate(msgDelegate)
|
require.Equal(t, expBalance, gotBalance, "expected account to have %d, got %d", expBalance, gotBalance)
|
||||||
//assert.Error(t, got, "expected error, got %v", got)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//// test that the delegator can still withdraw their bonds
|
func TestMultipleMsgDelegate(t *testing.T) {
|
||||||
//got = deliverer.unbond(msgUndelegate)
|
ctx, _, keeper := createTestInput(t, false, 1000)
|
||||||
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
candidateAddr, delegatorAddrs := addrs[0], addrs[1:]
|
||||||
|
|
||||||
//// verify that the pubkey can now be reused
|
//first make a candidate
|
||||||
//got = deliverer.declareCandidacy(msgDeclareCandidacy)
|
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[0], 10)
|
||||||
//assert.NoError(t, got, "expected ok, got %v", got)
|
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||||
//}
|
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
||||||
|
|
||||||
|
// delegate multiple parties
|
||||||
|
for i, delegatorAddr := range delegatorAddrs {
|
||||||
|
msgDelegate := newTestMsgDelegate(delegatorAddr, candidateAddr, 10)
|
||||||
|
got := handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||||
|
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||||
|
|
||||||
|
//Check that the account is bonded
|
||||||
|
bond, found := keeper.getDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||||
|
require.True(t, found)
|
||||||
|
require.NotNil(t, bond, "expected delegatee bond %d to exist", bond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unbond them all
|
||||||
|
for i, delegatorAddr := range delegatorAddrs {
|
||||||
|
msgUnbond := NewMsgUnbond(delegatorAddr, candidateAddr, "10")
|
||||||
|
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||||
|
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||||
|
|
||||||
|
//Check that the account is unbonded
|
||||||
|
_, found := keeper.getDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||||
|
require.False(t, found)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVoidCandidacy(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 1000)
|
||||||
|
candidateAddr, delegatorAddr := addrs[0], addrs[1]
|
||||||
|
|
||||||
|
// create the candidate
|
||||||
|
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[0], 10)
|
||||||
|
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||||
|
require.True(t, got.IsOK(), "expected no error on runMsgDeclareCandidacy")
|
||||||
|
|
||||||
|
// bond a delegator
|
||||||
|
msgDelegate := newTestMsgDelegate(delegatorAddr, candidateAddr, 10)
|
||||||
|
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||||
|
require.True(t, got.IsOK(), "expected ok, got %v", got)
|
||||||
|
|
||||||
|
// unbond the candidates bond portion
|
||||||
|
msgUnbondCandidate := NewMsgUnbond(candidateAddr, candidateAddr, "10")
|
||||||
|
got = handleMsgUnbond(ctx, msgUnbondCandidate, keeper)
|
||||||
|
require.True(t, got.IsOK(), "expected no error on runMsgDeclareCandidacy")
|
||||||
|
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||||
|
require.True(t, found)
|
||||||
|
require.Equal(t, Revoked, candidate.Status)
|
||||||
|
|
||||||
|
// test that this address cannot yet be bonded too because is revoked
|
||||||
|
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||||
|
assert.False(t, got.IsOK(), "expected error, got %v", got)
|
||||||
|
|
||||||
|
// test that the delegator can still withdraw their bonds
|
||||||
|
msgUnbondDelegator := NewMsgUnbond(delegatorAddr, candidateAddr, "10")
|
||||||
|
got = handleMsgUnbond(ctx, msgUnbondDelegator, keeper)
|
||||||
|
require.True(t, got.IsOK(), "expected no error on runMsgDeclareCandidacy")
|
||||||
|
|
||||||
|
// verify that the pubkey can now be reused
|
||||||
|
got = handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||||
|
assert.True(t, got.IsOK(), "expected ok, got %v", got)
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package stake
|
package stake
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// keeper of the staking store
|
// keeper of the staking store
|
||||||
|
@ -26,6 +30,17 @@ func NewKeeper(ctx sdk.Context, cdc *wire.Codec, key sdk.StoreKey, ck bank.CoinK
|
||||||
return keeper
|
return keeper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitGenesis - store genesis parameters
|
||||||
|
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.setPool(ctx, state.Pool)
|
||||||
|
k.setParams(ctx, state.Params)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
//_________________________________________________________________________
|
//_________________________________________________________________________
|
||||||
|
|
||||||
// get a single candidate
|
// get a single candidate
|
||||||
|
@ -81,30 +96,47 @@ func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) {
|
||||||
store.Set(GetCandidateKey(candidate.Address), bz)
|
store.Set(GetCandidateKey(candidate.Address), bz)
|
||||||
|
|
||||||
// mashal the new validator record
|
// mashal the new validator record
|
||||||
validator := Validator{address, candidate.Assets}
|
validator := candidate.validator()
|
||||||
bz, err = k.cdc.MarshalBinary(validator)
|
bz, err = k.cdc.MarshalBinary(validator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the voting power is the same no need to update any of the other indexes
|
||||||
|
if oldFound && oldCandidate.Assets.Equal(candidate.Assets) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// update the list ordered by voting power
|
// update the list ordered by voting power
|
||||||
if oldFound {
|
if oldFound {
|
||||||
store.Delete(GetValidatorKey(address, oldCandidate.Assets, k.cdc))
|
store.Delete(GetValidatorKey(address, oldCandidate.Assets, k.cdc))
|
||||||
}
|
}
|
||||||
store.Set(GetValidatorKey(address, validator.VotingPower, k.cdc), bz)
|
store.Set(GetValidatorKey(address, validator.Power, k.cdc), bz)
|
||||||
|
|
||||||
// add to the validators to update list if is already a validator
|
// add to the validators to update list if is already a validator
|
||||||
if store.Get(GetRecentValidatorKey(address)) == nil {
|
// or is a new validator
|
||||||
return
|
setAcc := false
|
||||||
}
|
if store.Get(GetRecentValidatorKey(address)) != nil {
|
||||||
store.Set(GetAccUpdateValidatorKey(validator.Address), bz)
|
setAcc = true
|
||||||
|
|
||||||
|
// want to check in the else statement because inefficient
|
||||||
|
} else if k.isNewValidator(ctx, store, address) {
|
||||||
|
setAcc = true
|
||||||
|
}
|
||||||
|
if setAcc {
|
||||||
|
bz, err = k.cdc.MarshalBinary(validator.abciValidator(k.cdc))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
store.Set(GetAccUpdateValidatorKey(validator.Address), bz)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) {
|
func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) {
|
||||||
|
|
||||||
// first retreive the old candidate record
|
// first retreive the old candidate record
|
||||||
oldCandidate, found := k.GetCandidate(ctx, address)
|
candidate, found := k.GetCandidate(ctx, address)
|
||||||
if !found {
|
if !found {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -112,39 +144,104 @@ func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) {
|
||||||
// delete the old candidate record
|
// delete the old candidate record
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
store.Delete(GetCandidateKey(address))
|
store.Delete(GetCandidateKey(address))
|
||||||
|
store.Delete(GetValidatorKey(address, candidate.Assets, k.cdc))
|
||||||
|
|
||||||
// delete from recent and power weighted validator groups if the validator
|
// delete from recent and power weighted validator groups if the validator
|
||||||
// exists and add validator with zero power to the validator updates
|
// exists and add validator with zero power to the validator updates
|
||||||
if store.Get(GetRecentValidatorKey(address)) == nil {
|
if store.Get(GetRecentValidatorKey(address)) == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bz, err := k.cdc.MarshalBinary(Validator{address, sdk.ZeroRat})
|
bz, err := k.cdc.MarshalBinary(candidate.validator().abciValidatorZero(k.cdc))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
store.Set(GetAccUpdateValidatorKey(address), bz)
|
store.Set(GetAccUpdateValidatorKey(address), bz)
|
||||||
store.Delete(GetRecentValidatorKey(address))
|
store.Delete(GetRecentValidatorKey(address))
|
||||||
store.Delete(GetValidatorKey(address, oldCandidate.Assets, k.cdc))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//___________________________________________________________________________
|
//___________________________________________________________________________
|
||||||
|
|
||||||
// get the most recent updated validator set from the Candidates. These bonds
|
// Get the validator set from the candidates. The correct subset is retrieved
|
||||||
// are already sorted by Assets from the UpdateVotingPower function which
|
// by iterating through an index of the candidates sorted by power, stored
|
||||||
// is the only function which is to modify the Assets
|
// using the ValidatorsKey. Simultaniously the most recent the validator
|
||||||
// this function also updaates the most recent validators saved in store
|
// records are updated in store with the RecentValidatorsKey. This store is
|
||||||
|
// used to determine if a candidate is a validator without needing to iterate
|
||||||
|
// over the subspace as we do in GetValidators
|
||||||
func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) {
|
func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
|
||||||
// clear the recent validators store
|
// clear the recent validators store, add to the ToKickOut Temp store
|
||||||
k.deleteSubSpace(store, RecentValidatorsKey)
|
iterator := store.Iterator(subspace(RecentValidatorsKey))
|
||||||
|
for ; iterator.Valid(); iterator.Next() {
|
||||||
|
addr := AddrFromKey(iterator.Key())
|
||||||
|
|
||||||
|
// iterator.Value is the validator object
|
||||||
|
store.Set(GetToKickOutValidatorKey(addr), iterator.Value())
|
||||||
|
store.Delete(iterator.Key())
|
||||||
|
}
|
||||||
|
iterator.Close()
|
||||||
|
|
||||||
// add the actual validator power sorted store
|
// add the actual validator power sorted store
|
||||||
maxVal := k.GetParams(ctx).MaxValidators
|
maxValidators := k.GetParams(ctx).MaxValidators
|
||||||
iterator := store.ReverseIterator(subspace(ValidatorsKey)) //smallest to largest
|
iterator = store.ReverseIterator(subspace(ValidatorsKey)) // largest to smallest
|
||||||
validators = make([]Validator, maxVal)
|
validators = make([]Validator, maxValidators)
|
||||||
i := 0
|
i := 0
|
||||||
for ; ; i++ {
|
for ; ; i++ {
|
||||||
|
if !iterator.Valid() || i > int(maxValidators-1) {
|
||||||
|
iterator.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
bz := iterator.Value()
|
||||||
|
var validator Validator
|
||||||
|
err := k.cdc.UnmarshalBinary(bz, &validator)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
validators[i] = validator
|
||||||
|
|
||||||
|
// remove from ToKickOut group
|
||||||
|
store.Delete(GetToKickOutValidatorKey(validator.Address))
|
||||||
|
|
||||||
|
// also add to the recent validators group
|
||||||
|
store.Set(GetRecentValidatorKey(validator.Address), bz)
|
||||||
|
|
||||||
|
iterator.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// add any kicked out validators to the acc change
|
||||||
|
iterator = store.Iterator(subspace(ToKickOutValidatorsKey))
|
||||||
|
for ; iterator.Valid(); iterator.Next() {
|
||||||
|
key := iterator.Key()
|
||||||
|
addr := AddrFromKey(key)
|
||||||
|
|
||||||
|
// get the zero abci validator from the ToKickOut iterator value
|
||||||
|
bz := iterator.Value()
|
||||||
|
var validator Validator
|
||||||
|
err := k.cdc.UnmarshalBinary(bz, &validator)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
bz, err = k.cdc.MarshalBinary(validator.abciValidatorZero(k.cdc))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
store.Set(GetAccUpdateValidatorKey(addr), bz)
|
||||||
|
store.Delete(key)
|
||||||
|
}
|
||||||
|
iterator.Close()
|
||||||
|
|
||||||
|
return validators[:i] // trim
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO this is madly inefficient because need to call every time we set a candidate
|
||||||
|
// Should use something better than an iterator maybe?
|
||||||
|
// Used to determine if something has just been added to the actual validator set
|
||||||
|
func (k Keeper) isNewValidator(ctx sdk.Context, store sdk.KVStore, address sdk.Address) bool {
|
||||||
|
// add the actual validator power sorted store
|
||||||
|
maxVal := k.GetParams(ctx).MaxValidators
|
||||||
|
iterator := store.ReverseIterator(subspace(ValidatorsKey)) // largest to smallest
|
||||||
|
for i := 0; ; i++ {
|
||||||
if !iterator.Valid() || i > int(maxVal-1) {
|
if !iterator.Valid() || i > int(maxVal-1) {
|
||||||
iterator.Close()
|
iterator.Close()
|
||||||
break
|
break
|
||||||
|
@ -155,15 +252,13 @@ func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
validators[i] = val
|
if bytes.Equal(val.Address, address) {
|
||||||
|
return true
|
||||||
// also add to the recent validators group
|
}
|
||||||
store.Set(GetRecentValidatorKey(val.Address), bz)
|
|
||||||
|
|
||||||
iterator.Next()
|
iterator.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
return validators[:i] // trim
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the address provided a part of the most recently saved validator group?
|
// Is the address provided a part of the most recently saved validator group?
|
||||||
|
@ -179,13 +274,13 @@ func (k Keeper) IsRecentValidator(ctx sdk.Context, address sdk.Address) bool {
|
||||||
// Accumulated updates to the validator set
|
// Accumulated updates to the validator set
|
||||||
|
|
||||||
// get the most recently updated validators
|
// get the most recently updated validators
|
||||||
func (k Keeper) getAccUpdateValidators(ctx sdk.Context) (updates []Validator) {
|
func (k Keeper) getAccUpdateValidators(ctx sdk.Context) (updates []abci.Validator) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
|
||||||
iterator := store.Iterator(subspace(AccUpdateValidatorsKey)) //smallest to largest
|
iterator := store.Iterator(subspace(AccUpdateValidatorsKey)) //smallest to largest
|
||||||
for ; iterator.Valid(); iterator.Next() {
|
for ; iterator.Valid(); iterator.Next() {
|
||||||
valBytes := iterator.Value()
|
valBytes := iterator.Value()
|
||||||
var val Validator
|
var val abci.Validator
|
||||||
err := k.cdc.UnmarshalBinary(valBytes, &val)
|
err := k.cdc.UnmarshalBinary(valBytes, &val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -199,12 +294,9 @@ func (k Keeper) getAccUpdateValidators(ctx sdk.Context) (updates []Validator) {
|
||||||
// remove all validator update entries
|
// remove all validator update entries
|
||||||
func (k Keeper) clearAccUpdateValidators(ctx sdk.Context) {
|
func (k Keeper) clearAccUpdateValidators(ctx sdk.Context) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
k.deleteSubSpace(store, AccUpdateValidatorsKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO move to common functionality somewhere
|
// delete subspace
|
||||||
func (k Keeper) deleteSubSpace(store sdk.KVStore, key []byte) {
|
iterator := store.Iterator(subspace(AccUpdateValidatorsKey))
|
||||||
iterator := store.Iterator(subspace(key))
|
|
||||||
for ; iterator.Valid(); iterator.Next() {
|
for ; iterator.Valid(); iterator.Next() {
|
||||||
store.Delete(iterator.Key())
|
store.Delete(iterator.Key())
|
||||||
}
|
}
|
||||||
|
@ -279,8 +371,7 @@ func (k Keeper) GetParams(ctx sdk.Context) (params Params) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
b := store.Get(ParamKey)
|
b := store.Get(ParamKey)
|
||||||
if b == nil {
|
if b == nil {
|
||||||
k.params = defaultParams()
|
panic("Stored params should not have been nil")
|
||||||
return k.params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := k.cdc.UnmarshalBinary(b, ¶ms)
|
err := k.cdc.UnmarshalBinary(b, ¶ms)
|
||||||
|
@ -298,3 +389,33 @@ func (k Keeper) setParams(ctx sdk.Context, params Params) {
|
||||||
store.Set(ParamKey, b)
|
store.Set(ParamKey, b)
|
||||||
k.params = Params{} // clear the cache
|
k.params = Params{} // clear the cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//_______________________________________________________________________
|
||||||
|
|
||||||
|
// load/save the pool
|
||||||
|
func (k Keeper) GetPool(ctx sdk.Context) (gs Pool) {
|
||||||
|
// check if cached before anything
|
||||||
|
if k.gs != (Pool{}) {
|
||||||
|
return k.gs
|
||||||
|
}
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
b := store.Get(PoolKey)
|
||||||
|
if b == nil {
|
||||||
|
panic("Stored pool should not have been nil")
|
||||||
|
}
|
||||||
|
err := k.cdc.UnmarshalBinary(b, &gs)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // This error should never occur big problem if does
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Keeper) setPool(ctx sdk.Context, p Pool) {
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
b, err := k.cdc.MarshalBinary(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
store.Set(PoolKey, b)
|
||||||
|
k.gs = Pool{} // clear the cache
|
||||||
|
}
|
||||||
|
|
|
@ -15,9 +15,11 @@ var (
|
||||||
CandidatesKey = []byte{0x02} // prefix for each key to a candidate
|
CandidatesKey = []byte{0x02} // prefix for each key to a candidate
|
||||||
ValidatorsKey = []byte{0x03} // prefix for each key to a validator
|
ValidatorsKey = []byte{0x03} // prefix for each key to a validator
|
||||||
AccUpdateValidatorsKey = []byte{0x04} // prefix for each key to a validator which is being updated
|
AccUpdateValidatorsKey = []byte{0x04} // prefix for each key to a validator which is being updated
|
||||||
RecentValidatorsKey = []byte{0x04} // prefix for each key to the last updated validator group
|
RecentValidatorsKey = []byte{0x05} // prefix for each key to the last updated validator group
|
||||||
|
|
||||||
DelegatorBondKeyPrefix = []byte{0x05} // prefix for each key to a delegator's bond
|
ToKickOutValidatorsKey = []byte{0x06} // prefix for each key to the last updated validator group
|
||||||
|
|
||||||
|
DelegatorBondKeyPrefix = []byte{0x07} // prefix for each key to a delegator's bond
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
|
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
|
||||||
|
@ -43,6 +45,16 @@ func GetRecentValidatorKey(addr sdk.Address) []byte {
|
||||||
return append(RecentValidatorsKey, addr.Bytes()...)
|
return append(RecentValidatorsKey, addr.Bytes()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reverse operation of GetRecentValidatorKey
|
||||||
|
func AddrFromKey(key []byte) sdk.Address {
|
||||||
|
return key[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the key for the accumulated update validators
|
||||||
|
func GetToKickOutValidatorKey(addr sdk.Address) []byte {
|
||||||
|
return append(ToKickOutValidatorsKey, addr.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
// get the key for delegator bond with candidate
|
// get the key for delegator bond with candidate
|
||||||
func GetDelegatorBondKey(delegatorAddr, candidateAddr sdk.Address, cdc *wire.Codec) []byte {
|
func GetDelegatorBondKey(delegatorAddr, candidateAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||||
return append(GetDelegatorBondsKey(delegatorAddr, cdc), candidateAddr.Bytes()...)
|
return append(GetDelegatorBondsKey(delegatorAddr, cdc), candidateAddr.Bytes()...)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package stake
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
@ -12,38 +13,34 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
addrDel1 = addrs[0]
|
addrDels = []sdk.Address{
|
||||||
addrDel2 = addrs[1]
|
addrs[0],
|
||||||
addrVal1 = addrs[2]
|
addrs[1],
|
||||||
addrVal2 = addrs[3]
|
|
||||||
addrVal3 = addrs[4]
|
|
||||||
pk1 = crypto.GenPrivKeyEd25519().PubKey()
|
|
||||||
pk2 = crypto.GenPrivKeyEd25519().PubKey()
|
|
||||||
pk3 = crypto.GenPrivKeyEd25519().PubKey()
|
|
||||||
|
|
||||||
candidate1 = Candidate{
|
|
||||||
Address: addrVal1,
|
|
||||||
PubKey: pk1,
|
|
||||||
Assets: sdk.NewRat(9),
|
|
||||||
Liabilities: sdk.NewRat(9),
|
|
||||||
}
|
}
|
||||||
candidate2 = Candidate{
|
addrVals = []sdk.Address{
|
||||||
Address: addrVal2,
|
addrs[2],
|
||||||
PubKey: pk2,
|
addrs[3],
|
||||||
Assets: sdk.NewRat(9),
|
addrs[4],
|
||||||
Liabilities: sdk.NewRat(9),
|
addrs[5],
|
||||||
}
|
addrs[6],
|
||||||
candidate3 = Candidate{
|
|
||||||
Address: addrVal3,
|
|
||||||
PubKey: pk3,
|
|
||||||
Assets: sdk.NewRat(9),
|
|
||||||
Liabilities: sdk.NewRat(9),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// This function tests GetCandidate, GetCandidates, setCandidate, removeCandidate
|
// This function tests GetCandidate, GetCandidates, setCandidate, removeCandidate
|
||||||
func TestCandidate(t *testing.T) {
|
func TestCandidate(t *testing.T) {
|
||||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
|
//construct the candidates
|
||||||
|
var candidates [3]Candidate
|
||||||
|
amts := []int64{9, 8, 7}
|
||||||
|
for i, amt := range amts {
|
||||||
|
candidates[i] = Candidate{
|
||||||
|
Address: addrVals[i],
|
||||||
|
PubKey: pks[i],
|
||||||
|
Assets: sdk.NewRat(amt),
|
||||||
|
Liabilities: sdk.NewRat(amt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
candidatesEqual := func(c1, c2 Candidate) bool {
|
candidatesEqual := func(c1, c2 Candidate) bool {
|
||||||
return c1.Status == c2.Status &&
|
return c1.Status == c2.Status &&
|
||||||
|
@ -55,60 +52,72 @@ func TestCandidate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the empty keeper first
|
// check the empty keeper first
|
||||||
_, found := keeper.GetCandidate(ctx, addrVal1)
|
_, found := keeper.GetCandidate(ctx, addrVals[0])
|
||||||
assert.False(t, found)
|
assert.False(t, found)
|
||||||
resCands := keeper.GetCandidates(ctx, 100)
|
resCands := keeper.GetCandidates(ctx, 100)
|
||||||
assert.Zero(t, len(resCands))
|
assert.Zero(t, len(resCands))
|
||||||
|
|
||||||
// set and retrieve a record
|
// set and retrieve a record
|
||||||
keeper.setCandidate(ctx, candidate1)
|
keeper.setCandidate(ctx, candidates[0])
|
||||||
resCand, found := keeper.GetCandidate(ctx, addrVal1)
|
resCand, found := keeper.GetCandidate(ctx, addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
assert.True(t, candidatesEqual(candidate1, resCand), "%v \n %v", resCand, candidate1)
|
assert.True(t, candidatesEqual(candidates[0], resCand), "%v \n %v", resCand, candidates[0])
|
||||||
|
|
||||||
// modify a records, save, and retrieve
|
// modify a records, save, and retrieve
|
||||||
candidate1.Liabilities = sdk.NewRat(99)
|
candidates[0].Liabilities = sdk.NewRat(99)
|
||||||
keeper.setCandidate(ctx, candidate1)
|
keeper.setCandidate(ctx, candidates[0])
|
||||||
resCand, found = keeper.GetCandidate(ctx, addrVal1)
|
resCand, found = keeper.GetCandidate(ctx, addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
assert.True(t, candidatesEqual(candidate1, resCand))
|
assert.True(t, candidatesEqual(candidates[0], resCand))
|
||||||
|
|
||||||
// also test that the address has been added to address list
|
// also test that the address has been added to address list
|
||||||
resCands = keeper.GetCandidates(ctx, 100)
|
resCands = keeper.GetCandidates(ctx, 100)
|
||||||
require.Equal(t, 1, len(resCands))
|
require.Equal(t, 1, len(resCands))
|
||||||
assert.Equal(t, addrVal1, resCands[0].Address)
|
assert.Equal(t, addrVals[0], resCands[0].Address)
|
||||||
|
|
||||||
// add other candidates
|
// add other candidates
|
||||||
keeper.setCandidate(ctx, candidate2)
|
keeper.setCandidate(ctx, candidates[1])
|
||||||
keeper.setCandidate(ctx, candidate3)
|
keeper.setCandidate(ctx, candidates[2])
|
||||||
resCand, found = keeper.GetCandidate(ctx, addrVal2)
|
resCand, found = keeper.GetCandidate(ctx, addrVals[1])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
assert.True(t, candidatesEqual(candidate2, resCand), "%v \n %v", resCand, candidate2)
|
assert.True(t, candidatesEqual(candidates[1], resCand), "%v \n %v", resCand, candidates[1])
|
||||||
resCand, found = keeper.GetCandidate(ctx, addrVal3)
|
resCand, found = keeper.GetCandidate(ctx, addrVals[2])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
assert.True(t, candidatesEqual(candidate3, resCand), "%v \n %v", resCand, candidate3)
|
assert.True(t, candidatesEqual(candidates[2], resCand), "%v \n %v", resCand, candidates[2])
|
||||||
resCands = keeper.GetCandidates(ctx, 100)
|
resCands = keeper.GetCandidates(ctx, 100)
|
||||||
require.Equal(t, 3, len(resCands))
|
require.Equal(t, 3, len(resCands))
|
||||||
assert.True(t, candidatesEqual(candidate1, resCands[0]), "%v \n %v", resCands[0], candidate1)
|
assert.True(t, candidatesEqual(candidates[0], resCands[0]), "%v \n %v", resCands[0], candidates[0])
|
||||||
assert.True(t, candidatesEqual(candidate2, resCands[1]), "%v \n %v", resCands[1], candidate2)
|
assert.True(t, candidatesEqual(candidates[1], resCands[1]), "%v \n %v", resCands[1], candidates[1])
|
||||||
assert.True(t, candidatesEqual(candidate3, resCands[2]), "%v \n %v", resCands[2], candidate3)
|
assert.True(t, candidatesEqual(candidates[2], resCands[2]), "%v \n %v", resCands[2], candidates[2])
|
||||||
|
|
||||||
// remove a record
|
// remove a record
|
||||||
keeper.removeCandidate(ctx, candidate2.Address)
|
keeper.removeCandidate(ctx, candidates[1].Address)
|
||||||
_, found = keeper.GetCandidate(ctx, addrVal2)
|
_, found = keeper.GetCandidate(ctx, addrVals[1])
|
||||||
assert.False(t, found)
|
assert.False(t, found)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tests GetDelegatorBond, GetDelegatorBonds, SetDelegatorBond, removeDelegatorBond
|
// tests GetDelegatorBond, GetDelegatorBonds, SetDelegatorBond, removeDelegatorBond
|
||||||
func TestBond(t *testing.T) {
|
func TestBond(t *testing.T) {
|
||||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
// first add a candidate1 to delegate too
|
//construct the candidates
|
||||||
keeper.setCandidate(ctx, candidate1)
|
amts := []int64{9, 8, 7}
|
||||||
|
var candidates [3]Candidate
|
||||||
|
for i, amt := range amts {
|
||||||
|
candidates[i] = Candidate{
|
||||||
|
Address: addrVals[i],
|
||||||
|
PubKey: pks[i],
|
||||||
|
Assets: sdk.NewRat(amt),
|
||||||
|
Liabilities: sdk.NewRat(amt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// first add a candidates[0] to delegate too
|
||||||
|
keeper.setCandidate(ctx, candidates[0])
|
||||||
|
|
||||||
bond1to1 := DelegatorBond{
|
bond1to1 := DelegatorBond{
|
||||||
DelegatorAddr: addrDel1,
|
DelegatorAddr: addrDels[0],
|
||||||
CandidateAddr: addrVal1,
|
CandidateAddr: addrVals[0],
|
||||||
Shares: sdk.NewRat(9),
|
Shares: sdk.NewRat(9),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,30 +128,30 @@ func TestBond(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the empty keeper first
|
// check the empty keeper first
|
||||||
_, found := keeper.getDelegatorBond(ctx, addrDel1, addrVal1)
|
_, found := keeper.getDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||||
assert.False(t, found)
|
assert.False(t, found)
|
||||||
|
|
||||||
// set and retrieve a record
|
// set and retrieve a record
|
||||||
keeper.setDelegatorBond(ctx, bond1to1)
|
keeper.setDelegatorBond(ctx, bond1to1)
|
||||||
resBond, found := keeper.getDelegatorBond(ctx, addrDel1, addrVal1)
|
resBond, found := keeper.getDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||||
assert.True(t, found)
|
assert.True(t, found)
|
||||||
assert.True(t, bondsEqual(bond1to1, resBond))
|
assert.True(t, bondsEqual(bond1to1, resBond))
|
||||||
|
|
||||||
// modify a records, save, and retrieve
|
// modify a records, save, and retrieve
|
||||||
bond1to1.Shares = sdk.NewRat(99)
|
bond1to1.Shares = sdk.NewRat(99)
|
||||||
keeper.setDelegatorBond(ctx, bond1to1)
|
keeper.setDelegatorBond(ctx, bond1to1)
|
||||||
resBond, found = keeper.getDelegatorBond(ctx, addrDel1, addrVal1)
|
resBond, found = keeper.getDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||||
assert.True(t, found)
|
assert.True(t, found)
|
||||||
assert.True(t, bondsEqual(bond1to1, resBond))
|
assert.True(t, bondsEqual(bond1to1, resBond))
|
||||||
|
|
||||||
// add some more records
|
// add some more records
|
||||||
keeper.setCandidate(ctx, candidate2)
|
keeper.setCandidate(ctx, candidates[1])
|
||||||
keeper.setCandidate(ctx, candidate3)
|
keeper.setCandidate(ctx, candidates[2])
|
||||||
bond1to2 := DelegatorBond{addrDel1, addrVal2, sdk.NewRat(9)}
|
bond1to2 := DelegatorBond{addrDels[0], addrVals[1], sdk.NewRat(9)}
|
||||||
bond1to3 := DelegatorBond{addrDel1, addrVal3, sdk.NewRat(9)}
|
bond1to3 := DelegatorBond{addrDels[0], addrVals[2], sdk.NewRat(9)}
|
||||||
bond2to1 := DelegatorBond{addrDel2, addrVal1, sdk.NewRat(9)}
|
bond2to1 := DelegatorBond{addrDels[1], addrVals[0], sdk.NewRat(9)}
|
||||||
bond2to2 := DelegatorBond{addrDel2, addrVal2, sdk.NewRat(9)}
|
bond2to2 := DelegatorBond{addrDels[1], addrVals[1], sdk.NewRat(9)}
|
||||||
bond2to3 := DelegatorBond{addrDel2, addrVal3, sdk.NewRat(9)}
|
bond2to3 := DelegatorBond{addrDels[1], addrVals[2], sdk.NewRat(9)}
|
||||||
keeper.setDelegatorBond(ctx, bond1to2)
|
keeper.setDelegatorBond(ctx, bond1to2)
|
||||||
keeper.setDelegatorBond(ctx, bond1to3)
|
keeper.setDelegatorBond(ctx, bond1to3)
|
||||||
keeper.setDelegatorBond(ctx, bond2to1)
|
keeper.setDelegatorBond(ctx, bond2to1)
|
||||||
|
@ -150,16 +159,16 @@ func TestBond(t *testing.T) {
|
||||||
keeper.setDelegatorBond(ctx, bond2to3)
|
keeper.setDelegatorBond(ctx, bond2to3)
|
||||||
|
|
||||||
// test all bond retrieve capabilities
|
// test all bond retrieve capabilities
|
||||||
resBonds := keeper.getDelegatorBonds(ctx, addrDel1, 5)
|
resBonds := keeper.getDelegatorBonds(ctx, addrDels[0], 5)
|
||||||
require.Equal(t, 3, len(resBonds))
|
require.Equal(t, 3, len(resBonds))
|
||||||
assert.True(t, bondsEqual(bond1to1, resBonds[0]))
|
assert.True(t, bondsEqual(bond1to1, resBonds[0]))
|
||||||
assert.True(t, bondsEqual(bond1to2, resBonds[1]))
|
assert.True(t, bondsEqual(bond1to2, resBonds[1]))
|
||||||
assert.True(t, bondsEqual(bond1to3, resBonds[2]))
|
assert.True(t, bondsEqual(bond1to3, resBonds[2]))
|
||||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel1, 3)
|
resBonds = keeper.getDelegatorBonds(ctx, addrDels[0], 3)
|
||||||
require.Equal(t, 3, len(resBonds))
|
require.Equal(t, 3, len(resBonds))
|
||||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel1, 2)
|
resBonds = keeper.getDelegatorBonds(ctx, addrDels[0], 2)
|
||||||
require.Equal(t, 2, len(resBonds))
|
require.Equal(t, 2, len(resBonds))
|
||||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel2, 5)
|
resBonds = keeper.getDelegatorBonds(ctx, addrDels[1], 5)
|
||||||
require.Equal(t, 3, len(resBonds))
|
require.Equal(t, 3, len(resBonds))
|
||||||
assert.True(t, bondsEqual(bond2to1, resBonds[0]))
|
assert.True(t, bondsEqual(bond2to1, resBonds[0]))
|
||||||
assert.True(t, bondsEqual(bond2to2, resBonds[1]))
|
assert.True(t, bondsEqual(bond2to2, resBonds[1]))
|
||||||
|
@ -167,9 +176,9 @@ func TestBond(t *testing.T) {
|
||||||
|
|
||||||
// delete a record
|
// delete a record
|
||||||
keeper.removeDelegatorBond(ctx, bond2to3)
|
keeper.removeDelegatorBond(ctx, bond2to3)
|
||||||
_, found = keeper.getDelegatorBond(ctx, addrDel2, addrVal3)
|
_, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[2])
|
||||||
assert.False(t, found)
|
assert.False(t, found)
|
||||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel2, 5)
|
resBonds = keeper.getDelegatorBonds(ctx, addrDels[1], 5)
|
||||||
require.Equal(t, 2, len(resBonds))
|
require.Equal(t, 2, len(resBonds))
|
||||||
assert.True(t, bondsEqual(bond2to1, resBonds[0]))
|
assert.True(t, bondsEqual(bond2to1, resBonds[0]))
|
||||||
assert.True(t, bondsEqual(bond2to2, resBonds[1]))
|
assert.True(t, bondsEqual(bond2to2, resBonds[1]))
|
||||||
|
@ -177,30 +186,30 @@ func TestBond(t *testing.T) {
|
||||||
// delete all the records from delegator 2
|
// delete all the records from delegator 2
|
||||||
keeper.removeDelegatorBond(ctx, bond2to1)
|
keeper.removeDelegatorBond(ctx, bond2to1)
|
||||||
keeper.removeDelegatorBond(ctx, bond2to2)
|
keeper.removeDelegatorBond(ctx, bond2to2)
|
||||||
_, found = keeper.getDelegatorBond(ctx, addrDel2, addrVal1)
|
_, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[0])
|
||||||
assert.False(t, found)
|
assert.False(t, found)
|
||||||
_, found = keeper.getDelegatorBond(ctx, addrDel2, addrVal2)
|
_, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[1])
|
||||||
assert.False(t, found)
|
assert.False(t, found)
|
||||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel2, 5)
|
resBonds = keeper.getDelegatorBonds(ctx, addrDels[1], 5)
|
||||||
require.Equal(t, 0, len(resBonds))
|
require.Equal(t, 0, len(resBonds))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO integrate in testing for equal validators, whichever one was a validator
|
// TODO integrate in testing for equal validators, whichever one was a validator
|
||||||
// first remains the validator https://github.com/cosmos/cosmos-sdk/issues/582
|
// first remains the validator https://github.com/cosmos/cosmos-sdk/issues/582
|
||||||
func TestGetValidators(t *testing.T) {
|
func TestGetValidators(t *testing.T) {
|
||||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
// initialize some candidates into the state
|
// initialize some candidates into the state
|
||||||
amts := []int64{0, 100, 1, 400, 200}
|
amts := []int64{0, 100, 1, 400, 200}
|
||||||
n := len(amts)
|
n := len(amts)
|
||||||
candidates := make([]Candidate, n)
|
var candidates [5]Candidate
|
||||||
for i := 0; i < n; i++ {
|
for i, amt := range amts {
|
||||||
c := Candidate{
|
c := Candidate{
|
||||||
Status: Unbonded,
|
Status: Unbonded,
|
||||||
PubKey: pks[i],
|
PubKey: pks[i],
|
||||||
Address: addrs[i],
|
Address: addrs[i],
|
||||||
Assets: sdk.NewRat(amts[i]),
|
Assets: sdk.NewRat(amt),
|
||||||
Liabilities: sdk.NewRat(amts[i]),
|
Liabilities: sdk.NewRat(amt),
|
||||||
}
|
}
|
||||||
keeper.setCandidate(ctx, c)
|
keeper.setCandidate(ctx, c)
|
||||||
candidates[i] = c
|
candidates[i] = c
|
||||||
|
@ -209,11 +218,11 @@ func TestGetValidators(t *testing.T) {
|
||||||
// first make sure everything as normal is ordered
|
// first make sure everything as normal is ordered
|
||||||
validators := keeper.GetValidators(ctx)
|
validators := keeper.GetValidators(ctx)
|
||||||
require.Equal(t, len(validators), n)
|
require.Equal(t, len(validators), n)
|
||||||
assert.Equal(t, sdk.NewRat(400), validators[0].VotingPower, "%v", validators)
|
assert.Equal(t, sdk.NewRat(400), validators[0].Power, "%v", validators)
|
||||||
assert.Equal(t, sdk.NewRat(200), validators[1].VotingPower, "%v", validators)
|
assert.Equal(t, sdk.NewRat(200), validators[1].Power, "%v", validators)
|
||||||
assert.Equal(t, sdk.NewRat(100), validators[2].VotingPower, "%v", validators)
|
assert.Equal(t, sdk.NewRat(100), validators[2].Power, "%v", validators)
|
||||||
assert.Equal(t, sdk.NewRat(1), validators[3].VotingPower, "%v", validators)
|
assert.Equal(t, sdk.NewRat(1), validators[3].Power, "%v", validators)
|
||||||
assert.Equal(t, sdk.NewRat(0), validators[4].VotingPower, "%v", validators)
|
assert.Equal(t, sdk.NewRat(0), validators[4].Power, "%v", validators)
|
||||||
assert.Equal(t, candidates[3].Address, validators[0].Address, "%v", validators)
|
assert.Equal(t, candidates[3].Address, validators[0].Address, "%v", validators)
|
||||||
assert.Equal(t, candidates[4].Address, validators[1].Address, "%v", validators)
|
assert.Equal(t, candidates[4].Address, validators[1].Address, "%v", validators)
|
||||||
assert.Equal(t, candidates[1].Address, validators[2].Address, "%v", validators)
|
assert.Equal(t, candidates[1].Address, validators[2].Address, "%v", validators)
|
||||||
|
@ -225,7 +234,7 @@ func TestGetValidators(t *testing.T) {
|
||||||
keeper.setCandidate(ctx, candidates[3])
|
keeper.setCandidate(ctx, candidates[3])
|
||||||
validators = keeper.GetValidators(ctx)
|
validators = keeper.GetValidators(ctx)
|
||||||
require.Equal(t, len(validators), n)
|
require.Equal(t, len(validators), n)
|
||||||
assert.Equal(t, sdk.NewRat(500), validators[0].VotingPower, "%v", validators)
|
assert.Equal(t, sdk.NewRat(500), validators[0].Power, "%v", validators)
|
||||||
assert.Equal(t, candidates[3].Address, validators[0].Address, "%v", validators)
|
assert.Equal(t, candidates[3].Address, validators[0].Address, "%v", validators)
|
||||||
|
|
||||||
// test a decrease in voting power
|
// test a decrease in voting power
|
||||||
|
@ -233,7 +242,7 @@ func TestGetValidators(t *testing.T) {
|
||||||
keeper.setCandidate(ctx, candidates[3])
|
keeper.setCandidate(ctx, candidates[3])
|
||||||
validators = keeper.GetValidators(ctx)
|
validators = keeper.GetValidators(ctx)
|
||||||
require.Equal(t, len(validators), n)
|
require.Equal(t, len(validators), n)
|
||||||
assert.Equal(t, sdk.NewRat(300), validators[0].VotingPower, "%v", validators)
|
assert.Equal(t, sdk.NewRat(300), validators[0].Power, "%v", validators)
|
||||||
assert.Equal(t, candidates[3].Address, validators[0].Address, "%v", validators)
|
assert.Equal(t, candidates[3].Address, validators[0].Address, "%v", validators)
|
||||||
|
|
||||||
// test a swap in voting power
|
// test a swap in voting power
|
||||||
|
@ -241,9 +250,9 @@ func TestGetValidators(t *testing.T) {
|
||||||
keeper.setCandidate(ctx, candidates[0])
|
keeper.setCandidate(ctx, candidates[0])
|
||||||
validators = keeper.GetValidators(ctx)
|
validators = keeper.GetValidators(ctx)
|
||||||
require.Equal(t, len(validators), n)
|
require.Equal(t, len(validators), n)
|
||||||
assert.Equal(t, sdk.NewRat(600), validators[0].VotingPower, "%v", validators)
|
assert.Equal(t, sdk.NewRat(600), validators[0].Power, "%v", validators)
|
||||||
assert.Equal(t, candidates[0].Address, validators[0].Address, "%v", validators)
|
assert.Equal(t, candidates[0].Address, validators[0].Address, "%v", validators)
|
||||||
assert.Equal(t, sdk.NewRat(300), validators[1].VotingPower, "%v", validators)
|
assert.Equal(t, sdk.NewRat(300), validators[1].Power, "%v", validators)
|
||||||
assert.Equal(t, candidates[3].Address, validators[1].Address, "%v", validators)
|
assert.Equal(t, candidates[3].Address, validators[1].Address, "%v", validators)
|
||||||
|
|
||||||
// test the max validators term
|
// test the max validators term
|
||||||
|
@ -253,46 +262,301 @@ func TestGetValidators(t *testing.T) {
|
||||||
keeper.setParams(ctx, params)
|
keeper.setParams(ctx, params)
|
||||||
validators = keeper.GetValidators(ctx)
|
validators = keeper.GetValidators(ctx)
|
||||||
require.Equal(t, len(validators), n)
|
require.Equal(t, len(validators), n)
|
||||||
assert.Equal(t, sdk.NewRat(600), validators[0].VotingPower, "%v", validators)
|
assert.Equal(t, sdk.NewRat(600), validators[0].Power, "%v", validators)
|
||||||
assert.Equal(t, candidates[0].Address, validators[0].Address, "%v", validators)
|
assert.Equal(t, candidates[0].Address, validators[0].Address, "%v", validators)
|
||||||
assert.Equal(t, sdk.NewRat(300), validators[1].VotingPower, "%v", validators)
|
assert.Equal(t, sdk.NewRat(300), validators[1].Power, "%v", validators)
|
||||||
assert.Equal(t, candidates[3].Address, validators[1].Address, "%v", validators)
|
assert.Equal(t, candidates[3].Address, validators[1].Address, "%v", validators)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
|
||||||
// test the mechanism which keeps track of a validator set change
|
|
||||||
func TestGetAccUpdateValidators(t *testing.T) {
|
|
||||||
//TODO
|
|
||||||
// test from nothing to something
|
|
||||||
// test from something to nothing
|
|
||||||
// test identical
|
|
||||||
// test single value change
|
|
||||||
// test multiple value change
|
|
||||||
// test validator added at the beginning
|
|
||||||
// test validator added in the middle
|
|
||||||
// test validator added at the end
|
|
||||||
// test multiple validators removed
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear the tracked changes to the validator set
|
// clear the tracked changes to the validator set
|
||||||
func TestClearAccUpdateValidators(t *testing.T) {
|
func TestClearAccUpdateValidators(t *testing.T) {
|
||||||
//TODO
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
|
amts := []int64{100, 400, 200}
|
||||||
|
candidates := make([]Candidate, len(amts))
|
||||||
|
for i, amt := range amts {
|
||||||
|
c := Candidate{
|
||||||
|
Status: Unbonded,
|
||||||
|
PubKey: pks[i],
|
||||||
|
Address: addrs[i],
|
||||||
|
Assets: sdk.NewRat(amt),
|
||||||
|
Liabilities: sdk.NewRat(amt),
|
||||||
|
}
|
||||||
|
candidates[i] = c
|
||||||
|
keeper.setCandidate(ctx, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
acc := keeper.getAccUpdateValidators(ctx)
|
||||||
|
assert.Equal(t, len(amts), len(acc))
|
||||||
|
keeper.clearAccUpdateValidators(ctx)
|
||||||
|
acc = keeper.getAccUpdateValidators(ctx)
|
||||||
|
assert.Equal(t, 0, len(acc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// test the mechanism which keeps track of a validator set change
|
||||||
|
func TestGetAccUpdateValidators(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
params := defaultParams()
|
||||||
|
params.MaxValidators = 4
|
||||||
|
keeper.setParams(ctx, params)
|
||||||
|
|
||||||
|
// TODO eliminate use of candidatesIn here
|
||||||
|
// tests could be clearer if they just
|
||||||
|
// created the candidate at time of use
|
||||||
|
// and were labelled by power in the comments
|
||||||
|
// outlining in each test
|
||||||
|
amts := []int64{10, 11, 12, 13, 1}
|
||||||
|
var candidatesIn [5]Candidate
|
||||||
|
for i, amt := range amts {
|
||||||
|
candidatesIn[i] = Candidate{
|
||||||
|
Address: addrs[i],
|
||||||
|
PubKey: pks[i],
|
||||||
|
Assets: sdk.NewRat(amt),
|
||||||
|
Liabilities: sdk.NewRat(amt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to compare pubkeys between abci pubkey and crypto.PubKey
|
||||||
|
wirePK := func(pk crypto.PubKey) []byte {
|
||||||
|
pkBytes, err := keeper.cdc.MarshalBinary(pk)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return pkBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// test from nothing to something
|
||||||
|
// candidate set: {} -> {c1, c3}
|
||||||
|
// validator set: {} -> {c1, c3}
|
||||||
|
// accUpdate set: {} -> {c1, c3}
|
||||||
|
assert.Equal(t, 0, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 0, len(keeper.GetValidators(ctx)))
|
||||||
|
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||||
|
|
||||||
|
keeper.setCandidate(ctx, candidatesIn[1])
|
||||||
|
keeper.setCandidate(ctx, candidatesIn[3])
|
||||||
|
|
||||||
|
vals := keeper.GetValidators(ctx) // to init recent validator set
|
||||||
|
require.Equal(t, 2, len(vals))
|
||||||
|
acc := keeper.getAccUpdateValidators(ctx)
|
||||||
|
require.Equal(t, 2, len(acc))
|
||||||
|
candidates := keeper.GetCandidates(ctx, 5)
|
||||||
|
require.Equal(t, 2, len(candidates))
|
||||||
|
assert.Equal(t, candidates[0].validator().abciValidator(keeper.cdc), acc[0])
|
||||||
|
assert.Equal(t, candidates[1].validator().abciValidator(keeper.cdc), acc[1])
|
||||||
|
assert.Equal(t, candidates[0].validator(), vals[1])
|
||||||
|
assert.Equal(t, candidates[1].validator(), vals[0])
|
||||||
|
|
||||||
|
// test identical,
|
||||||
|
// candidate set: {c1, c3} -> {c1, c3}
|
||||||
|
// accUpdate set: {} -> {}
|
||||||
|
keeper.clearAccUpdateValidators(ctx)
|
||||||
|
assert.Equal(t, 2, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||||
|
|
||||||
|
keeper.setCandidate(ctx, candidates[0])
|
||||||
|
keeper.setCandidate(ctx, candidates[1])
|
||||||
|
|
||||||
|
require.Equal(t, 2, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||||
|
|
||||||
|
// test single value change
|
||||||
|
// candidate set: {c1, c3} -> {c1', c3}
|
||||||
|
// accUpdate set: {} -> {c1'}
|
||||||
|
keeper.clearAccUpdateValidators(ctx)
|
||||||
|
assert.Equal(t, 2, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||||
|
|
||||||
|
candidates[0].Assets = sdk.NewRat(600)
|
||||||
|
keeper.setCandidate(ctx, candidates[0])
|
||||||
|
|
||||||
|
candidates = keeper.GetCandidates(ctx, 5)
|
||||||
|
require.Equal(t, 2, len(candidates))
|
||||||
|
assert.True(t, candidates[0].Assets.Equal(sdk.NewRat(600)))
|
||||||
|
acc = keeper.getAccUpdateValidators(ctx)
|
||||||
|
require.Equal(t, 1, len(acc))
|
||||||
|
assert.Equal(t, candidates[0].validator().abciValidator(keeper.cdc), acc[0])
|
||||||
|
|
||||||
|
// test multiple value change
|
||||||
|
// candidate set: {c1, c3} -> {c1', c3'}
|
||||||
|
// accUpdate set: {c1, c3} -> {c1', c3'}
|
||||||
|
keeper.clearAccUpdateValidators(ctx)
|
||||||
|
assert.Equal(t, 2, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||||
|
|
||||||
|
candidates[0].Assets = sdk.NewRat(200)
|
||||||
|
candidates[1].Assets = sdk.NewRat(100)
|
||||||
|
keeper.setCandidate(ctx, candidates[0])
|
||||||
|
keeper.setCandidate(ctx, candidates[1])
|
||||||
|
|
||||||
|
acc = keeper.getAccUpdateValidators(ctx)
|
||||||
|
require.Equal(t, 2, len(acc))
|
||||||
|
candidates = keeper.GetCandidates(ctx, 5)
|
||||||
|
require.Equal(t, 2, len(candidates))
|
||||||
|
require.Equal(t, candidates[0].validator().abciValidator(keeper.cdc), acc[0])
|
||||||
|
require.Equal(t, candidates[1].validator().abciValidator(keeper.cdc), acc[1])
|
||||||
|
|
||||||
|
// test validtor added at the beginning
|
||||||
|
// candidate set: {c1, c3} -> {c0, c1, c3}
|
||||||
|
// accUpdate set: {} -> {c0}
|
||||||
|
keeper.clearAccUpdateValidators(ctx)
|
||||||
|
assert.Equal(t, 2, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||||
|
|
||||||
|
keeper.setCandidate(ctx, candidatesIn[0])
|
||||||
|
acc = keeper.getAccUpdateValidators(ctx)
|
||||||
|
require.Equal(t, 1, len(acc))
|
||||||
|
candidates = keeper.GetCandidates(ctx, 5)
|
||||||
|
require.Equal(t, 3, len(candidates))
|
||||||
|
assert.Equal(t, candidates[0].validator().abciValidator(keeper.cdc), acc[0])
|
||||||
|
|
||||||
|
// test validator added at the middle
|
||||||
|
// candidate set: {c0, c1, c3} -> {c0, c1, c2, c3]
|
||||||
|
// accUpdate set: {} -> {c2}
|
||||||
|
keeper.clearAccUpdateValidators(ctx)
|
||||||
|
assert.Equal(t, 3, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||||
|
|
||||||
|
keeper.setCandidate(ctx, candidatesIn[2])
|
||||||
|
acc = keeper.getAccUpdateValidators(ctx)
|
||||||
|
require.Equal(t, 1, len(acc))
|
||||||
|
candidates = keeper.GetCandidates(ctx, 5)
|
||||||
|
require.Equal(t, 4, len(candidates))
|
||||||
|
assert.Equal(t, candidates[2].validator().abciValidator(keeper.cdc), acc[0])
|
||||||
|
|
||||||
|
// test candidate added at the end but not inserted in the valset
|
||||||
|
// candidate set: {c0, c1, c2, c3} -> {c0, c1, c2, c3, c4}
|
||||||
|
// validator set: {c0, c1, c2, c3} -> {c0, c1, c2, c3}
|
||||||
|
// accUpdate set: {} -> {}
|
||||||
|
keeper.clearAccUpdateValidators(ctx)
|
||||||
|
assert.Equal(t, 4, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||||
|
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||||
|
|
||||||
|
keeper.setCandidate(ctx, candidatesIn[4])
|
||||||
|
|
||||||
|
assert.Equal(t, 5, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||||
|
require.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx))) // max validator number is 4
|
||||||
|
|
||||||
|
// test candidate change its power but still not in the valset
|
||||||
|
// candidate set: {c0, c1, c2, c3, c4} -> {c0, c1, c2, c3, c4}
|
||||||
|
// validator set: {c0, c1, c2, c3} -> {c0, c1, c2, c3}
|
||||||
|
// accUpdate set: {} -> {}
|
||||||
|
keeper.clearAccUpdateValidators(ctx)
|
||||||
|
assert.Equal(t, 5, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||||
|
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||||
|
|
||||||
|
candidatesIn[4].Assets = sdk.NewRat(1)
|
||||||
|
keeper.setCandidate(ctx, candidatesIn[4])
|
||||||
|
|
||||||
|
assert.Equal(t, 5, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||||
|
require.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx))) // max validator number is 4
|
||||||
|
|
||||||
|
// test candidate change its power and become a validator (pushing out an existing)
|
||||||
|
// candidate set: {c0, c1, c2, c3, c4} -> {c0, c1, c2, c3, c4}
|
||||||
|
// validator set: {c0, c1, c2, c3} -> {c1, c2, c3, c4}
|
||||||
|
// accUpdate set: {} -> {c0, c4}
|
||||||
|
keeper.clearAccUpdateValidators(ctx)
|
||||||
|
assert.Equal(t, 5, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||||
|
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||||
|
|
||||||
|
candidatesIn[4].Assets = sdk.NewRat(1000)
|
||||||
|
keeper.setCandidate(ctx, candidatesIn[4])
|
||||||
|
|
||||||
|
candidates = keeper.GetCandidates(ctx, 5)
|
||||||
|
require.Equal(t, 5, len(candidates))
|
||||||
|
vals = keeper.GetValidators(ctx)
|
||||||
|
require.Equal(t, 4, len(vals))
|
||||||
|
assert.Equal(t, candidatesIn[1].Address, vals[1].Address)
|
||||||
|
assert.Equal(t, candidatesIn[2].Address, vals[3].Address)
|
||||||
|
assert.Equal(t, candidatesIn[3].Address, vals[2].Address)
|
||||||
|
assert.Equal(t, candidatesIn[4].Address, vals[0].Address)
|
||||||
|
|
||||||
|
acc = keeper.getAccUpdateValidators(ctx)
|
||||||
|
require.Equal(t, 2, len(acc), "%v", acc)
|
||||||
|
|
||||||
|
assert.Equal(t, wirePK(candidatesIn[0].PubKey), acc[0].PubKey)
|
||||||
|
assert.Equal(t, int64(0), acc[0].Power)
|
||||||
|
assert.Equal(t, vals[0].abciValidator(keeper.cdc), acc[1])
|
||||||
|
|
||||||
|
// test from something to nothing
|
||||||
|
// candidate set: {c0, c1, c2, c3, c4} -> {}
|
||||||
|
// validator set: {c1, c2, c3, c4} -> {}
|
||||||
|
// accUpdate set: {} -> {c1, c2, c3, c4}
|
||||||
|
keeper.clearAccUpdateValidators(ctx)
|
||||||
|
assert.Equal(t, 5, len(keeper.GetCandidates(ctx, 5)))
|
||||||
|
assert.Equal(t, 4, len(keeper.GetValidators(ctx)))
|
||||||
|
assert.Equal(t, 0, len(keeper.getAccUpdateValidators(ctx)))
|
||||||
|
|
||||||
|
keeper.removeCandidate(ctx, candidatesIn[0].Address)
|
||||||
|
keeper.removeCandidate(ctx, candidatesIn[1].Address)
|
||||||
|
keeper.removeCandidate(ctx, candidatesIn[2].Address)
|
||||||
|
keeper.removeCandidate(ctx, candidatesIn[3].Address)
|
||||||
|
keeper.removeCandidate(ctx, candidatesIn[4].Address)
|
||||||
|
|
||||||
|
vals = keeper.GetValidators(ctx)
|
||||||
|
assert.Equal(t, 0, len(vals), "%v", vals)
|
||||||
|
candidates = keeper.GetCandidates(ctx, 5)
|
||||||
|
require.Equal(t, 0, len(candidates))
|
||||||
|
acc = keeper.getAccUpdateValidators(ctx)
|
||||||
|
require.Equal(t, 4, len(acc))
|
||||||
|
assert.Equal(t, wirePK(candidatesIn[1].PubKey), acc[0].PubKey)
|
||||||
|
assert.Equal(t, wirePK(candidatesIn[2].PubKey), acc[1].PubKey)
|
||||||
|
assert.Equal(t, wirePK(candidatesIn[3].PubKey), acc[2].PubKey)
|
||||||
|
assert.Equal(t, wirePK(candidatesIn[4].PubKey), acc[3].PubKey)
|
||||||
|
assert.Equal(t, int64(0), acc[0].Power)
|
||||||
|
assert.Equal(t, int64(0), acc[1].Power)
|
||||||
|
assert.Equal(t, int64(0), acc[2].Power)
|
||||||
|
assert.Equal(t, int64(0), acc[3].Power)
|
||||||
}
|
}
|
||||||
|
|
||||||
// test if is a validator from the last update
|
// test if is a validator from the last update
|
||||||
func TestIsRecentValidator(t *testing.T) {
|
func TestIsRecentValidator(t *testing.T) {
|
||||||
//TODO
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
|
amts := []int64{9, 8, 7, 10, 6}
|
||||||
|
var candidatesIn [5]Candidate
|
||||||
|
for i, amt := range amts {
|
||||||
|
candidatesIn[i] = Candidate{
|
||||||
|
Address: addrVals[i],
|
||||||
|
PubKey: pks[i],
|
||||||
|
Assets: sdk.NewRat(amt),
|
||||||
|
Liabilities: sdk.NewRat(amt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// test that an empty validator set doesn't have any validators
|
// test that an empty validator set doesn't have any validators
|
||||||
|
validators := keeper.GetValidators(ctx)
|
||||||
|
assert.Equal(t, 0, len(validators))
|
||||||
|
|
||||||
// get the validators for the first time
|
// get the validators for the first time
|
||||||
|
keeper.setCandidate(ctx, candidatesIn[0])
|
||||||
|
keeper.setCandidate(ctx, candidatesIn[1])
|
||||||
|
validators = keeper.GetValidators(ctx)
|
||||||
|
require.Equal(t, 2, len(validators))
|
||||||
|
assert.Equal(t, candidatesIn[0].validator(), validators[0])
|
||||||
|
assert.Equal(t, candidatesIn[1].validator(), validators[1])
|
||||||
|
|
||||||
// test a basic retrieve of something that should be a recent validator
|
// test a basic retrieve of something that should be a recent validator
|
||||||
|
assert.True(t, keeper.IsRecentValidator(ctx, candidatesIn[0].Address))
|
||||||
|
assert.True(t, keeper.IsRecentValidator(ctx, candidatesIn[1].Address))
|
||||||
|
|
||||||
// test a basic retrieve of something that should not be a recent validator
|
// test a basic retrieve of something that should not be a recent validator
|
||||||
|
assert.False(t, keeper.IsRecentValidator(ctx, candidatesIn[2].Address))
|
||||||
|
|
||||||
// remove that validator, but don't retrieve the recent validator group
|
// remove that validator, but don't retrieve the recent validator group
|
||||||
|
keeper.removeCandidate(ctx, candidatesIn[0].Address)
|
||||||
|
|
||||||
// test that removed validator is not considered a recent validator
|
// test that removed validator is not considered a recent validator
|
||||||
|
assert.False(t, keeper.IsRecentValidator(ctx, candidatesIn[0].Address))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParams(t *testing.T) {
|
func TestParams(t *testing.T) {
|
||||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
expParams := defaultParams()
|
expParams := defaultParams()
|
||||||
|
|
||||||
//check that the empty keeper loads the default
|
//check that the empty keeper loads the default
|
||||||
|
@ -305,3 +569,46 @@ func TestParams(t *testing.T) {
|
||||||
resParams = keeper.GetParams(ctx)
|
resParams = keeper.GetParams(ctx)
|
||||||
assert.Equal(t, expParams, resParams)
|
assert.Equal(t, expParams, resParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPool(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
expPool := initialPool()
|
||||||
|
|
||||||
|
//check that the empty keeper loads the default
|
||||||
|
resPool := keeper.GetPool(ctx)
|
||||||
|
assert.Equal(t, expPool, resPool)
|
||||||
|
|
||||||
|
//modify a params, save, and retrieve
|
||||||
|
expPool.TotalSupply = 777
|
||||||
|
keeper.setPool(ctx, expPool)
|
||||||
|
resPool = keeper.GetPool(ctx)
|
||||||
|
assert.Equal(t, expPool, resPool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitGenesis(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
jsonStr := `{
|
||||||
|
"params": {
|
||||||
|
"inflation_rate_change": {"num": 13, "denom": 100},
|
||||||
|
"inflation_max": {"num": 20, "denom": 100},
|
||||||
|
"inflation_min": {"num": 7, "denom": 100},
|
||||||
|
"goal_bonded": {"num": 67, "denom": 100},
|
||||||
|
"max_validators": 100,
|
||||||
|
"bond_denom": "fermion"
|
||||||
|
},
|
||||||
|
"pool": {
|
||||||
|
"total_supply": 0,
|
||||||
|
"bonded_shares": {"num": 0, "denom": 1},
|
||||||
|
"unbonded_shares": {"num": 0, "denom": 1},
|
||||||
|
"bonded_pool": 0,
|
||||||
|
"unbonded_pool": 0,
|
||||||
|
"inflation_last_time": 0,
|
||||||
|
"inflation": {"num": 7, "denom": 100}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
encoded := json.RawMessage(jsonStr)
|
||||||
|
err := keeper.InitGenesis(ctx, encoded)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, keeper.GetPool(ctx), initialPool())
|
||||||
|
require.Equal(t, keeper.GetParams(ctx), defaultParams())
|
||||||
|
}
|
||||||
|
|
109
x/stake/pool.go
109
x/stake/pool.go
|
@ -4,132 +4,115 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// load/save the global staking state
|
// get the bond ratio of the global state
|
||||||
func (k Keeper) GetPool(ctx sdk.Context) (gs Pool) {
|
func (p Pool) bondedRatio() sdk.Rat {
|
||||||
// check if cached before anything
|
if p.TotalSupply > 0 {
|
||||||
if k.gs != (Pool{}) {
|
return sdk.NewRat(p.BondedPool, p.TotalSupply)
|
||||||
return k.gs
|
|
||||||
}
|
}
|
||||||
store := ctx.KVStore(k.storeKey)
|
return sdk.ZeroRat
|
||||||
b := store.Get(PoolKey)
|
|
||||||
if b == nil {
|
|
||||||
return initialPool()
|
|
||||||
}
|
|
||||||
err := k.cdc.UnmarshalBinary(b, &gs)
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // This error should never occur big problem if does
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) setPool(ctx sdk.Context, p Pool) {
|
// get the exchange rate of bonded token per issued share
|
||||||
store := ctx.KVStore(k.storeKey)
|
func (p Pool) bondedShareExRate() sdk.Rat {
|
||||||
b, err := k.cdc.MarshalBinary(p)
|
if p.BondedShares.IsZero() {
|
||||||
if err != nil {
|
return sdk.OneRat
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
store.Set(PoolKey, b)
|
return sdk.NewRat(p.BondedPool).Quo(p.BondedShares)
|
||||||
k.gs = Pool{} // clear the cache
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//_______________________________________________________________________
|
// get the exchange rate of unbonded tokens held in candidates per issued share
|
||||||
|
func (p Pool) unbondedShareExRate() sdk.Rat {
|
||||||
//TODO make these next two functions more efficient should be reading and writting to state ye know
|
if p.UnbondedShares.IsZero() {
|
||||||
|
return sdk.OneRat
|
||||||
|
}
|
||||||
|
return sdk.NewRat(p.UnbondedPool).Quo(p.UnbondedShares)
|
||||||
|
}
|
||||||
|
|
||||||
// move a candidates asset pool from bonded to unbonded pool
|
// move a candidates asset pool from bonded to unbonded pool
|
||||||
func (k Keeper) bondedToUnbondedPool(ctx sdk.Context, candidate Candidate) {
|
func (p Pool) bondedToUnbondedPool(candidate Candidate) (Pool, Candidate) {
|
||||||
|
|
||||||
// replace bonded shares with unbonded shares
|
// replace bonded shares with unbonded shares
|
||||||
tokens := k.removeSharesBonded(ctx, candidate.Assets)
|
p, tokens := p.removeSharesBonded(candidate.Assets)
|
||||||
candidate.Assets = k.addTokensUnbonded(ctx, tokens)
|
p, candidate.Assets = p.addTokensUnbonded(tokens)
|
||||||
candidate.Status = Unbonded
|
candidate.Status = Unbonded
|
||||||
k.setCandidate(ctx, candidate)
|
return p, candidate
|
||||||
}
|
}
|
||||||
|
|
||||||
// move a candidates asset pool from unbonded to bonded pool
|
// move a candidates asset pool from unbonded to bonded pool
|
||||||
func (k Keeper) unbondedToBondedPool(ctx sdk.Context, candidate Candidate) {
|
func (p Pool) unbondedToBondedPool(candidate Candidate) (Pool, Candidate) {
|
||||||
|
|
||||||
// replace unbonded shares with bonded shares
|
// replace unbonded shares with bonded shares
|
||||||
tokens := k.removeSharesUnbonded(ctx, candidate.Assets)
|
p, tokens := p.removeSharesUnbonded(candidate.Assets)
|
||||||
candidate.Assets = k.addTokensBonded(ctx, tokens)
|
p, candidate.Assets = p.addTokensBonded(tokens)
|
||||||
candidate.Status = Bonded
|
candidate.Status = Bonded
|
||||||
k.setCandidate(ctx, candidate)
|
return p, candidate
|
||||||
}
|
}
|
||||||
|
|
||||||
//_______________________________________________________________________
|
//_______________________________________________________________________
|
||||||
|
|
||||||
func (k Keeper) addTokensBonded(ctx sdk.Context, amount int64) (issuedShares sdk.Rat) {
|
func (p Pool) addTokensBonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||||
p := k.GetPool(ctx)
|
issuedShares = sdk.NewRat(amount).Quo(p.bondedShareExRate()) // (tokens/shares)^-1 * tokens
|
||||||
issuedShares = p.bondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
|
|
||||||
p.BondedPool += amount
|
p.BondedPool += amount
|
||||||
p.BondedShares = p.BondedShares.Add(issuedShares)
|
p.BondedShares = p.BondedShares.Add(issuedShares)
|
||||||
k.setPool(ctx, p)
|
return p, issuedShares
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) removeSharesBonded(ctx sdk.Context, shares sdk.Rat) (removedTokens int64) {
|
func (p Pool) removeSharesBonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||||
p := k.GetPool(ctx)
|
|
||||||
removedTokens = p.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
removedTokens = p.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||||
p.BondedShares = p.BondedShares.Sub(shares)
|
p.BondedShares = p.BondedShares.Sub(shares)
|
||||||
p.BondedPool -= removedTokens
|
p.BondedPool = p.BondedPool - removedTokens
|
||||||
k.setPool(ctx, p)
|
return p, removedTokens
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) addTokensUnbonded(ctx sdk.Context, amount int64) (issuedShares sdk.Rat) {
|
func (p Pool) addTokensUnbonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||||
p := k.GetPool(ctx)
|
|
||||||
issuedShares = p.unbondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
|
issuedShares = p.unbondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
|
||||||
p.UnbondedShares = p.UnbondedShares.Add(issuedShares)
|
p.UnbondedShares = p.UnbondedShares.Add(issuedShares)
|
||||||
p.UnbondedPool += amount
|
p.UnbondedPool += amount
|
||||||
k.setPool(ctx, p)
|
return p, issuedShares
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) removeSharesUnbonded(ctx sdk.Context, shares sdk.Rat) (removedTokens int64) {
|
func (p Pool) removeSharesUnbonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||||
p := k.GetPool(ctx)
|
|
||||||
removedTokens = p.unbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
removedTokens = p.unbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||||
p.UnbondedShares = p.UnbondedShares.Sub(shares)
|
p.UnbondedShares = p.UnbondedShares.Sub(shares)
|
||||||
p.UnbondedPool -= removedTokens
|
p.UnbondedPool -= removedTokens
|
||||||
k.setPool(ctx, p)
|
return p, removedTokens
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//_______________________________________________________________________
|
//_______________________________________________________________________
|
||||||
|
|
||||||
// add tokens to a candidate
|
// add tokens to a candidate
|
||||||
func (k Keeper) candidateAddTokens(ctx sdk.Context, candidate Candidate, amount int64) (issuedDelegatorShares sdk.Rat) {
|
func (p Pool) candidateAddTokens(candidate Candidate,
|
||||||
|
amount int64) (p2 Pool, candidate2 Candidate, issuedDelegatorShares sdk.Rat) {
|
||||||
|
|
||||||
p := k.GetPool(ctx)
|
|
||||||
exRate := candidate.delegatorShareExRate()
|
exRate := candidate.delegatorShareExRate()
|
||||||
|
|
||||||
var receivedGlobalShares sdk.Rat
|
var receivedGlobalShares sdk.Rat
|
||||||
if candidate.Status == Bonded {
|
if candidate.Status == Bonded {
|
||||||
receivedGlobalShares = k.addTokensBonded(ctx, amount)
|
p, receivedGlobalShares = p.addTokensBonded(amount)
|
||||||
} else {
|
} else {
|
||||||
receivedGlobalShares = k.addTokensUnbonded(ctx, amount)
|
p, receivedGlobalShares = p.addTokensUnbonded(amount)
|
||||||
}
|
}
|
||||||
candidate.Assets = candidate.Assets.Add(receivedGlobalShares)
|
candidate.Assets = candidate.Assets.Add(receivedGlobalShares)
|
||||||
|
|
||||||
issuedDelegatorShares = exRate.Mul(receivedGlobalShares)
|
issuedDelegatorShares = exRate.Mul(receivedGlobalShares)
|
||||||
candidate.Liabilities = candidate.Liabilities.Add(issuedDelegatorShares)
|
candidate.Liabilities = candidate.Liabilities.Add(issuedDelegatorShares)
|
||||||
k.setPool(ctx, p) // TODO cache Pool?
|
|
||||||
return
|
return p, candidate, issuedDelegatorShares
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove shares from a candidate
|
// remove shares from a candidate
|
||||||
func (k Keeper) candidateRemoveShares(ctx sdk.Context, candidate Candidate, shares sdk.Rat) (createdCoins int64) {
|
func (p Pool) candidateRemoveShares(candidate Candidate,
|
||||||
|
shares sdk.Rat) (p2 Pool, candidate2 Candidate, createdCoins int64) {
|
||||||
|
|
||||||
p := k.GetPool(ctx)
|
|
||||||
//exRate := candidate.delegatorShareExRate() //XXX make sure not used
|
//exRate := candidate.delegatorShareExRate() //XXX make sure not used
|
||||||
|
|
||||||
globalPoolSharesToRemove := candidate.delegatorShareExRate().Mul(shares)
|
globalPoolSharesToRemove := candidate.delegatorShareExRate().Mul(shares)
|
||||||
if candidate.Status == Bonded {
|
if candidate.Status == Bonded {
|
||||||
createdCoins = k.removeSharesBonded(ctx, globalPoolSharesToRemove)
|
p, createdCoins = p.removeSharesBonded(globalPoolSharesToRemove)
|
||||||
} else {
|
} else {
|
||||||
createdCoins = k.removeSharesUnbonded(ctx, globalPoolSharesToRemove)
|
p, createdCoins = p.removeSharesUnbonded(globalPoolSharesToRemove)
|
||||||
}
|
}
|
||||||
candidate.Assets = candidate.Assets.Sub(globalPoolSharesToRemove)
|
candidate.Assets = candidate.Assets.Sub(globalPoolSharesToRemove)
|
||||||
candidate.Liabilities = candidate.Liabilities.Sub(shares)
|
candidate.Liabilities = candidate.Liabilities.Sub(shares)
|
||||||
k.setPool(ctx, p) // TODO cache Pool?
|
return p, candidate, createdCoins
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,533 @@
|
||||||
package stake
|
package stake
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPool(t *testing.T) {
|
func TestBondedRatio(t *testing.T) {
|
||||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
expPool := initialPool()
|
pool := keeper.GetPool(ctx)
|
||||||
|
pool.TotalSupply = 3
|
||||||
|
pool.BondedPool = 2
|
||||||
|
|
||||||
//check that the empty keeper loads the default
|
// bonded pool / total supply
|
||||||
resPool := keeper.GetPool(ctx)
|
require.Equal(t, pool.bondedRatio(), sdk.NewRat(2).Quo(sdk.NewRat(3)))
|
||||||
assert.Equal(t, expPool, resPool)
|
pool.TotalSupply = 0
|
||||||
|
|
||||||
//modify a params, save, and retrieve
|
// avoids divide-by-zero
|
||||||
expPool.TotalSupply = 777
|
require.Equal(t, pool.bondedRatio(), sdk.ZeroRat)
|
||||||
keeper.setPool(ctx, expPool)
|
}
|
||||||
resPool = keeper.GetPool(ctx)
|
|
||||||
assert.Equal(t, expPool, resPool)
|
func TestBondedShareExRate(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
pool := keeper.GetPool(ctx)
|
||||||
|
pool.BondedPool = 3
|
||||||
|
pool.BondedShares = sdk.NewRat(10)
|
||||||
|
|
||||||
|
// bonded pool / bonded shares
|
||||||
|
require.Equal(t, pool.bondedShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
||||||
|
pool.BondedShares = sdk.ZeroRat
|
||||||
|
|
||||||
|
// avoids divide-by-zero
|
||||||
|
require.Equal(t, pool.bondedShareExRate(), sdk.OneRat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnbondedShareExRate(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
pool := keeper.GetPool(ctx)
|
||||||
|
pool.UnbondedPool = 3
|
||||||
|
pool.UnbondedShares = sdk.NewRat(10)
|
||||||
|
|
||||||
|
// unbonded pool / unbonded shares
|
||||||
|
require.Equal(t, pool.unbondedShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
||||||
|
pool.UnbondedShares = sdk.ZeroRat
|
||||||
|
|
||||||
|
// avoids divide-by-zero
|
||||||
|
require.Equal(t, pool.unbondedShareExRate(), sdk.OneRat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBondedToUnbondedPool(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
|
poolA := keeper.GetPool(ctx)
|
||||||
|
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
|
||||||
|
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
|
||||||
|
candA := Candidate{
|
||||||
|
Status: Bonded,
|
||||||
|
Address: addrs[0],
|
||||||
|
PubKey: pks[0],
|
||||||
|
Assets: sdk.OneRat,
|
||||||
|
Liabilities: sdk.OneRat,
|
||||||
|
}
|
||||||
|
poolB, candB := poolA.bondedToUnbondedPool(candA)
|
||||||
|
|
||||||
|
// status unbonded
|
||||||
|
assert.Equal(t, candB.Status, Unbonded)
|
||||||
|
// same exchange rate, assets unchanged
|
||||||
|
assert.Equal(t, candB.Assets, candA.Assets)
|
||||||
|
// bonded pool decreased
|
||||||
|
assert.Equal(t, poolB.BondedPool, poolA.BondedPool-candA.Assets.Evaluate())
|
||||||
|
// unbonded pool increased
|
||||||
|
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool+candA.Assets.Evaluate())
|
||||||
|
// conservation of tokens
|
||||||
|
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool, poolA.BondedPool+poolA.UnbondedPool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnbonbedtoBondedPool(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
|
poolA := keeper.GetPool(ctx)
|
||||||
|
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
|
||||||
|
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
|
||||||
|
candA := Candidate{
|
||||||
|
Status: Bonded,
|
||||||
|
Address: addrs[0],
|
||||||
|
PubKey: pks[0],
|
||||||
|
Assets: sdk.OneRat,
|
||||||
|
Liabilities: sdk.OneRat,
|
||||||
|
}
|
||||||
|
candA.Status = Unbonded
|
||||||
|
poolB, candB := poolA.unbondedToBondedPool(candA)
|
||||||
|
|
||||||
|
// status bonded
|
||||||
|
assert.Equal(t, candB.Status, Bonded)
|
||||||
|
// same exchange rate, assets unchanged
|
||||||
|
assert.Equal(t, candB.Assets, candA.Assets)
|
||||||
|
// bonded pool increased
|
||||||
|
assert.Equal(t, poolB.BondedPool, poolA.BondedPool+candA.Assets.Evaluate())
|
||||||
|
// unbonded pool decreased
|
||||||
|
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool-candA.Assets.Evaluate())
|
||||||
|
// conservation of tokens
|
||||||
|
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool, poolA.BondedPool+poolA.UnbondedPool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddTokensBonded(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
|
poolA := keeper.GetPool(ctx)
|
||||||
|
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
|
||||||
|
poolB, sharesB := poolA.addTokensBonded(10)
|
||||||
|
assert.Equal(t, poolB.bondedShareExRate(), sdk.OneRat)
|
||||||
|
|
||||||
|
// correct changes to bonded shares and bonded pool
|
||||||
|
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Add(sharesB))
|
||||||
|
assert.Equal(t, poolB.BondedPool, poolA.BondedPool+10)
|
||||||
|
|
||||||
|
// same number of bonded shares / tokens when exchange rate is one
|
||||||
|
assert.Equal(t, poolB.BondedShares, sdk.NewRat(poolB.BondedPool))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveSharesBonded(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
|
poolA := keeper.GetPool(ctx)
|
||||||
|
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
|
||||||
|
poolB, tokensB := poolA.removeSharesBonded(sdk.NewRat(10))
|
||||||
|
assert.Equal(t, poolB.bondedShareExRate(), sdk.OneRat)
|
||||||
|
|
||||||
|
// correct changes to bonded shares and bonded pool
|
||||||
|
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Sub(sdk.NewRat(10)))
|
||||||
|
assert.Equal(t, poolB.BondedPool, poolA.BondedPool-tokensB)
|
||||||
|
|
||||||
|
// same number of bonded shares / tokens when exchange rate is one
|
||||||
|
assert.Equal(t, poolB.BondedShares, sdk.NewRat(poolB.BondedPool))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddTokensUnbonded(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
|
poolA := keeper.GetPool(ctx)
|
||||||
|
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
|
||||||
|
poolB, sharesB := poolA.addTokensUnbonded(10)
|
||||||
|
assert.Equal(t, poolB.unbondedShareExRate(), sdk.OneRat)
|
||||||
|
|
||||||
|
// correct changes to unbonded shares and unbonded pool
|
||||||
|
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Add(sharesB))
|
||||||
|
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool+10)
|
||||||
|
|
||||||
|
// same number of unbonded shares / tokens when exchange rate is one
|
||||||
|
assert.Equal(t, poolB.UnbondedShares, sdk.NewRat(poolB.UnbondedPool))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveSharesUnbonded(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
|
poolA := keeper.GetPool(ctx)
|
||||||
|
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
|
||||||
|
poolB, tokensB := poolA.removeSharesUnbonded(sdk.NewRat(10))
|
||||||
|
assert.Equal(t, poolB.unbondedShareExRate(), sdk.OneRat)
|
||||||
|
|
||||||
|
// correct changes to unbonded shares and bonded pool
|
||||||
|
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Sub(sdk.NewRat(10)))
|
||||||
|
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool-tokensB)
|
||||||
|
|
||||||
|
// same number of unbonded shares / tokens when exchange rate is one
|
||||||
|
assert.Equal(t, poolB.UnbondedShares, sdk.NewRat(poolB.UnbondedPool))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCandidateAddTokens(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
|
poolA := keeper.GetPool(ctx)
|
||||||
|
candA := Candidate{
|
||||||
|
Status: Bonded,
|
||||||
|
Address: addrs[0],
|
||||||
|
PubKey: pks[0],
|
||||||
|
Assets: sdk.NewRat(9),
|
||||||
|
Liabilities: sdk.NewRat(9),
|
||||||
|
}
|
||||||
|
poolA.BondedPool = candA.Assets.Evaluate()
|
||||||
|
poolA.BondedShares = candA.Assets
|
||||||
|
assert.Equal(t, candA.delegatorShareExRate(), sdk.OneRat)
|
||||||
|
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
|
||||||
|
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
|
||||||
|
poolB, candB, sharesB := poolA.candidateAddTokens(candA, 10)
|
||||||
|
|
||||||
|
// shares were issued
|
||||||
|
assert.Equal(t, sdk.NewRat(10).Mul(candA.delegatorShareExRate()), sharesB)
|
||||||
|
// pool shares were added
|
||||||
|
assert.Equal(t, candB.Assets, candA.Assets.Add(sdk.NewRat(10)))
|
||||||
|
// conservation of tokens
|
||||||
|
assert.Equal(t, poolB.BondedPool, 10+poolA.BondedPool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCandidateRemoveShares(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
|
|
||||||
|
poolA := keeper.GetPool(ctx)
|
||||||
|
candA := Candidate{
|
||||||
|
Status: Bonded,
|
||||||
|
Address: addrs[0],
|
||||||
|
PubKey: pks[0],
|
||||||
|
Assets: sdk.NewRat(9),
|
||||||
|
Liabilities: sdk.NewRat(9),
|
||||||
|
}
|
||||||
|
poolA.BondedPool = candA.Assets.Evaluate()
|
||||||
|
poolA.BondedShares = candA.Assets
|
||||||
|
assert.Equal(t, candA.delegatorShareExRate(), sdk.OneRat)
|
||||||
|
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat)
|
||||||
|
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat)
|
||||||
|
poolB, candB, coinsB := poolA.candidateRemoveShares(candA, sdk.NewRat(10))
|
||||||
|
|
||||||
|
// coins were created
|
||||||
|
assert.Equal(t, coinsB, int64(10))
|
||||||
|
// pool shares were removed
|
||||||
|
assert.Equal(t, candB.Assets, candA.Assets.Sub(sdk.NewRat(10).Mul(candA.delegatorShareExRate())))
|
||||||
|
// conservation of tokens
|
||||||
|
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool+coinsB, poolA.UnbondedPool+poolA.BondedPool)
|
||||||
|
|
||||||
|
// specific case from random tests
|
||||||
|
assets := sdk.NewRat(5102)
|
||||||
|
liabilities := sdk.NewRat(115)
|
||||||
|
cand := Candidate{
|
||||||
|
Status: Bonded,
|
||||||
|
Address: addrs[0],
|
||||||
|
PubKey: pks[0],
|
||||||
|
Assets: assets,
|
||||||
|
Liabilities: liabilities,
|
||||||
|
}
|
||||||
|
pool := Pool{
|
||||||
|
TotalSupply: 0,
|
||||||
|
BondedShares: sdk.NewRat(248305),
|
||||||
|
UnbondedShares: sdk.NewRat(232147),
|
||||||
|
BondedPool: 248305,
|
||||||
|
UnbondedPool: 232147,
|
||||||
|
InflationLastTime: 0,
|
||||||
|
Inflation: sdk.NewRat(7, 100),
|
||||||
|
}
|
||||||
|
shares := sdk.NewRat(29)
|
||||||
|
msg := fmt.Sprintf("candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
msg = fmt.Sprintf("Removed %v shares from %s", shares, msg)
|
||||||
|
newPool, _, tokens := pool.candidateRemoveShares(cand, shares)
|
||||||
|
require.Equal(t,
|
||||||
|
tokens+newPool.UnbondedPool+newPool.BondedPool,
|
||||||
|
pool.BondedPool+pool.UnbondedPool,
|
||||||
|
"Tokens were not conserved: %s", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
// TODO Make all random tests less obfuscated!
|
||||||
|
|
||||||
|
// generate a random candidate
|
||||||
|
func randomCandidate(r *rand.Rand) Candidate {
|
||||||
|
var status CandidateStatus
|
||||||
|
if r.Float64() < float64(0.5) {
|
||||||
|
status = Bonded
|
||||||
|
} else {
|
||||||
|
status = Unbonded
|
||||||
|
}
|
||||||
|
assets := sdk.NewRat(int64(r.Int31n(10000)))
|
||||||
|
liabilities := sdk.NewRat(int64(r.Int31n(10000)))
|
||||||
|
return Candidate{
|
||||||
|
Status: status,
|
||||||
|
Address: addrs[0],
|
||||||
|
PubKey: pks[0],
|
||||||
|
Assets: assets,
|
||||||
|
Liabilities: liabilities,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a random staking state
|
||||||
|
func randomSetup(r *rand.Rand, numCandidates int) (Pool, Candidates) {
|
||||||
|
pool := Pool{
|
||||||
|
TotalSupply: 0,
|
||||||
|
BondedShares: sdk.ZeroRat,
|
||||||
|
UnbondedShares: sdk.ZeroRat,
|
||||||
|
BondedPool: 0,
|
||||||
|
UnbondedPool: 0,
|
||||||
|
InflationLastTime: 0,
|
||||||
|
Inflation: sdk.NewRat(7, 100),
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates := make([]Candidate, numCandidates)
|
||||||
|
for i := 0; i < numCandidates; i++ {
|
||||||
|
candidate := randomCandidate(r)
|
||||||
|
if candidate.Status == Bonded {
|
||||||
|
pool.BondedShares = pool.BondedShares.Add(candidate.Assets)
|
||||||
|
pool.BondedPool += candidate.Assets.Evaluate()
|
||||||
|
} else if candidate.Status == Unbonded {
|
||||||
|
pool.UnbondedShares = pool.UnbondedShares.Add(candidate.Assets)
|
||||||
|
pool.UnbondedPool += candidate.Assets.Evaluate()
|
||||||
|
}
|
||||||
|
candidates[i] = candidate
|
||||||
|
}
|
||||||
|
return pool, candidates
|
||||||
|
}
|
||||||
|
|
||||||
|
// any operation that transforms staking state
|
||||||
|
// takes in RNG instance, pool, candidate
|
||||||
|
// returns updated pool, updated candidate, delta tokens, descriptive message
|
||||||
|
type Operation func(r *rand.Rand, p Pool, c Candidate) (Pool, Candidate, int64, string)
|
||||||
|
|
||||||
|
// operation: bond or unbond a candidate depending on current status
|
||||||
|
func OpBondOrUnbond(r *rand.Rand, p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||||
|
var msg string
|
||||||
|
if cand.Status == Bonded {
|
||||||
|
msg = fmt.Sprintf("Unbonded previously bonded candidate %s (assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
cand.Address, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
p, cand = p.bondedToUnbondedPool(cand)
|
||||||
|
|
||||||
|
} else if cand.Status == Unbonded {
|
||||||
|
msg = fmt.Sprintf("Bonded previously unbonded candidate %s (assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
cand.Address, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
p, cand = p.unbondedToBondedPool(cand)
|
||||||
|
}
|
||||||
|
return p, cand, 0, msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// operation: add a random number of tokens to a candidate
|
||||||
|
func OpAddTokens(r *rand.Rand, p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||||
|
tokens := int64(r.Int31n(1000))
|
||||||
|
msg := fmt.Sprintf("candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
p, cand, _ = p.candidateAddTokens(cand, tokens)
|
||||||
|
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||||
|
return p, cand, -1 * tokens, msg // tokens are removed so for accounting must be negative
|
||||||
|
}
|
||||||
|
|
||||||
|
// operation: remove a random number of shares from a candidate
|
||||||
|
func OpRemoveShares(r *rand.Rand, p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||||
|
var shares sdk.Rat
|
||||||
|
for {
|
||||||
|
shares = sdk.NewRat(int64(r.Int31n(1000)))
|
||||||
|
if shares.LT(cand.Liabilities) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("Removed %v shares from candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
shares, cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
|
||||||
|
p, cand, tokens := p.candidateRemoveShares(cand, shares)
|
||||||
|
return p, cand, tokens, msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// pick a random staking operation
|
||||||
|
func randomOperation(r *rand.Rand) Operation {
|
||||||
|
operations := []Operation{
|
||||||
|
OpBondOrUnbond,
|
||||||
|
OpAddTokens,
|
||||||
|
OpRemoveShares,
|
||||||
|
}
|
||||||
|
r.Shuffle(len(operations), func(i, j int) {
|
||||||
|
operations[i], operations[j] = operations[j], operations[i]
|
||||||
|
})
|
||||||
|
return operations[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure invariants that should always be true are true
|
||||||
|
func assertInvariants(t *testing.T, msg string,
|
||||||
|
pOrig Pool, cOrig Candidates, pMod Pool, cMods Candidates, tokens int64) {
|
||||||
|
|
||||||
|
// total tokens conserved
|
||||||
|
require.Equal(t,
|
||||||
|
pOrig.UnbondedPool+pOrig.BondedPool,
|
||||||
|
pMod.UnbondedPool+pMod.BondedPool+tokens,
|
||||||
|
"Tokens not conserved - msg: %v\n, pOrig.BondedShares: %v, pOrig.UnbondedShares: %v, pMod.BondedShares: %v, pMod.UnbondedShares: %v, pOrig.UnbondedPool: %v, pOrig.BondedPool: %v, pMod.UnbondedPool: %v, pMod.BondedPool: %v, tokens: %v\n",
|
||||||
|
msg,
|
||||||
|
pOrig.BondedShares, pOrig.UnbondedShares,
|
||||||
|
pMod.BondedShares, pMod.UnbondedShares,
|
||||||
|
pOrig.UnbondedPool, pOrig.BondedPool,
|
||||||
|
pMod.UnbondedPool, pMod.BondedPool, tokens)
|
||||||
|
|
||||||
|
// nonnegative bonded shares
|
||||||
|
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat),
|
||||||
|
"Negative bonded shares - msg: %v\npOrig: %#v\npMod: %#v\ntokens: %v\n",
|
||||||
|
msg, pOrig, pMod, tokens)
|
||||||
|
|
||||||
|
// nonnegative unbonded shares
|
||||||
|
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat),
|
||||||
|
"Negative unbonded shares - msg: %v\npOrig: %#v\npMod: %#v\ntokens: %v\n",
|
||||||
|
msg, pOrig, pMod, tokens)
|
||||||
|
|
||||||
|
// nonnegative bonded ex rate
|
||||||
|
require.False(t, pMod.bondedShareExRate().LT(sdk.ZeroRat),
|
||||||
|
"Applying operation \"%s\" resulted in negative bondedShareExRate: %d",
|
||||||
|
msg, pMod.bondedShareExRate().Evaluate())
|
||||||
|
|
||||||
|
// nonnegative unbonded ex rate
|
||||||
|
require.False(t, pMod.unbondedShareExRate().LT(sdk.ZeroRat),
|
||||||
|
"Applying operation \"%s\" resulted in negative unbondedShareExRate: %d",
|
||||||
|
msg, pMod.unbondedShareExRate().Evaluate())
|
||||||
|
|
||||||
|
for _, cMod := range cMods {
|
||||||
|
|
||||||
|
// nonnegative ex rate
|
||||||
|
require.False(t, cMod.delegatorShareExRate().LT(sdk.ZeroRat),
|
||||||
|
"Applying operation \"%s\" resulted in negative candidate.delegatorShareExRate(): %v (candidate.Address: %s)",
|
||||||
|
msg,
|
||||||
|
cMod.delegatorShareExRate(),
|
||||||
|
cMod.Address,
|
||||||
|
)
|
||||||
|
|
||||||
|
// nonnegative assets
|
||||||
|
require.False(t, cMod.Assets.LT(sdk.ZeroRat),
|
||||||
|
"Applying operation \"%s\" resulted in negative candidate.Assets: %v (candidate.Liabilities: %v, candidate.delegatorShareExRate: %v, candidate.Address: %s)",
|
||||||
|
msg,
|
||||||
|
cMod.Assets,
|
||||||
|
cMod.Liabilities,
|
||||||
|
cMod.delegatorShareExRate(),
|
||||||
|
cMod.Address,
|
||||||
|
)
|
||||||
|
|
||||||
|
// nonnegative liabilities
|
||||||
|
require.False(t, cMod.Liabilities.LT(sdk.ZeroRat),
|
||||||
|
"Applying operation \"%s\" resulted in negative candidate.Liabilities: %v (candidate.Assets: %v, candidate.delegatorShareExRate: %v, candidate.Address: %s)",
|
||||||
|
msg,
|
||||||
|
cMod.Liabilities,
|
||||||
|
cMod.Assets,
|
||||||
|
cMod.delegatorShareExRate(),
|
||||||
|
cMod.Address,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Re-enable once the overflow bug is fixed!
|
||||||
|
// ref https://github.com/cosmos/cosmos-sdk/issues/753
|
||||||
|
/*
|
||||||
|
func TestPossibleOverflow(t *testing.T) {
|
||||||
|
assets := sdk.NewRat(2159)
|
||||||
|
liabilities := sdk.NewRat(391432570689183511).Quo(sdk.NewRat(40113011844664))
|
||||||
|
cand := Candidate{
|
||||||
|
Status: Bonded,
|
||||||
|
Address: addrs[0],
|
||||||
|
PubKey: pks[0],
|
||||||
|
Assets: assets,
|
||||||
|
Liabilities: liabilities,
|
||||||
|
}
|
||||||
|
pool := Pool{
|
||||||
|
TotalSupply: 0,
|
||||||
|
BondedShares: assets,
|
||||||
|
UnbondedShares: sdk.ZeroRat,
|
||||||
|
BondedPool: assets.Evaluate(),
|
||||||
|
UnbondedPool: 0,
|
||||||
|
InflationLastTime: 0,
|
||||||
|
Inflation: sdk.NewRat(7, 100),
|
||||||
|
}
|
||||||
|
tokens := int64(71)
|
||||||
|
msg := fmt.Sprintf("candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
_, newCandidate, _ := pool.candidateAddTokens(cand, tokens)
|
||||||
|
|
||||||
|
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||||
|
require.False(t, newCandidate.delegatorShareExRate().LT(sdk.ZeroRat),
|
||||||
|
"Applying operation \"%s\" resulted in negative delegatorShareExRate(): %v",
|
||||||
|
msg, newCandidate.delegatorShareExRate())
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// run random operations in a random order on a random single-candidate state, assert invariants hold
|
||||||
|
func TestSingleCandidateIntegrationInvariants(t *testing.T) {
|
||||||
|
r := rand.New(rand.NewSource(41))
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
poolOrig, candidatesOrig := randomSetup(r, 1)
|
||||||
|
require.Equal(t, 1, len(candidatesOrig))
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
assertInvariants(t, "no operation",
|
||||||
|
poolOrig, candidatesOrig,
|
||||||
|
poolOrig, candidatesOrig, 0)
|
||||||
|
|
||||||
|
// TODO Increase iteration count once overflow bug is fixed
|
||||||
|
// ref https://github.com/cosmos/cosmos-sdk/issues/753
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
poolMod, candidateMod, tokens, msg := randomOperation(r)(r, poolOrig, candidatesOrig[0])
|
||||||
|
|
||||||
|
candidatesMod := make([]Candidate, len(candidatesOrig))
|
||||||
|
copy(candidatesMod[:], candidatesOrig[:])
|
||||||
|
require.Equal(t, 1, len(candidatesOrig), "j %v", j)
|
||||||
|
require.Equal(t, 1, len(candidatesMod), "j %v", j)
|
||||||
|
candidatesMod[0] = candidateMod
|
||||||
|
|
||||||
|
assertInvariants(t, msg,
|
||||||
|
poolOrig, candidatesOrig,
|
||||||
|
poolMod, candidatesMod, tokens)
|
||||||
|
|
||||||
|
poolOrig = poolMod
|
||||||
|
candidatesOrig = candidatesMod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// run random operations in a random order on a random multi-candidate state, assert invariants hold
|
||||||
|
func TestMultiCandidateIntegrationInvariants(t *testing.T) {
|
||||||
|
r := rand.New(rand.NewSource(42))
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
poolOrig, candidatesOrig := randomSetup(r, 100)
|
||||||
|
|
||||||
|
assertInvariants(t, "no operation",
|
||||||
|
poolOrig, candidatesOrig,
|
||||||
|
poolOrig, candidatesOrig, 0)
|
||||||
|
|
||||||
|
// TODO Increase iteration count once overflow bug is fixed
|
||||||
|
// ref https://github.com/cosmos/cosmos-sdk/issues/753
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
index := int(r.Int31n(int32(len(candidatesOrig))))
|
||||||
|
poolMod, candidateMod, tokens, msg := randomOperation(r)(r, poolOrig, candidatesOrig[index])
|
||||||
|
candidatesMod := make([]Candidate, len(candidatesOrig))
|
||||||
|
copy(candidatesMod[:], candidatesOrig[:])
|
||||||
|
candidatesMod[index] = candidateMod
|
||||||
|
|
||||||
|
assertInvariants(t, msg,
|
||||||
|
poolOrig, candidatesOrig,
|
||||||
|
poolMod, candidatesMod, tokens)
|
||||||
|
|
||||||
|
poolOrig = poolMod
|
||||||
|
candidatesOrig = candidatesMod
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
oldwire "github.com/tendermint/go-wire"
|
oldwire "github.com/tendermint/go-wire"
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
"github.com/cosmos/cosmos-sdk/store"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
@ -52,6 +51,31 @@ var (
|
||||||
emptyPubkey crypto.PubKey
|
emptyPubkey crypto.PubKey
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// default params for testing
|
||||||
|
func defaultParams() Params {
|
||||||
|
return Params{
|
||||||
|
InflationRateChange: sdk.NewRat(13, 100),
|
||||||
|
InflationMax: sdk.NewRat(20, 100),
|
||||||
|
InflationMin: sdk.NewRat(7, 100),
|
||||||
|
GoalBonded: sdk.NewRat(67, 100),
|
||||||
|
MaxValidators: 100,
|
||||||
|
BondDenom: "fermion",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial pool for testing
|
||||||
|
func initialPool() Pool {
|
||||||
|
return Pool{
|
||||||
|
TotalSupply: 0,
|
||||||
|
BondedShares: sdk.ZeroRat,
|
||||||
|
UnbondedShares: sdk.ZeroRat,
|
||||||
|
BondedPool: 0,
|
||||||
|
UnbondedPool: 0,
|
||||||
|
InflationLastTime: 0,
|
||||||
|
Inflation: sdk.NewRat(7, 100),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// XXX reference the common declaration of this function
|
// XXX reference the common declaration of this function
|
||||||
func subspace(prefix []byte) (start, end []byte) {
|
func subspace(prefix []byte) (start, end []byte) {
|
||||||
end = make([]byte, len(prefix))
|
end = make([]byte, len(prefix))
|
||||||
|
@ -83,7 +107,7 @@ func makeTestCodec() *wire.Codec {
|
||||||
const accTypeApp = 0x1
|
const accTypeApp = 0x1
|
||||||
var _ = oldwire.RegisterInterface(
|
var _ = oldwire.RegisterInterface(
|
||||||
struct{ sdk.Account }{},
|
struct{ sdk.Account }{},
|
||||||
oldwire.ConcreteType{&types.AppAccount{}, accTypeApp},
|
oldwire.ConcreteType{&auth.BaseAccount{}, accTypeApp},
|
||||||
)
|
)
|
||||||
cdc := wire.NewCodec()
|
cdc := wire.NewCodec()
|
||||||
|
|
||||||
|
@ -105,7 +129,7 @@ func paramsNoInflation() Params {
|
||||||
}
|
}
|
||||||
|
|
||||||
// hogpodge of all sorts of input required for testing
|
// hogpodge of all sorts of input required for testing
|
||||||
func createTestInput(t *testing.T, sender sdk.Address, isCheckTx bool, initCoins int64) (sdk.Context, sdk.AccountMapper, Keeper) {
|
func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context, sdk.AccountMapper, Keeper) {
|
||||||
db := dbm.NewMemDB()
|
db := dbm.NewMemDB()
|
||||||
keyStake := sdk.NewKVStoreKey("stake")
|
keyStake := sdk.NewKVStoreKey("stake")
|
||||||
keyMain := keyStake //sdk.NewKVStoreKey("main") //TODO fix multistore
|
keyMain := keyStake //sdk.NewKVStoreKey("main") //TODO fix multistore
|
||||||
|
@ -123,13 +147,14 @@ func createTestInput(t *testing.T, sender sdk.Address, isCheckTx bool, initCoins
|
||||||
)
|
)
|
||||||
ck := bank.NewCoinKeeper(accountMapper)
|
ck := bank.NewCoinKeeper(accountMapper)
|
||||||
keeper := NewKeeper(ctx, cdc, keyStake, ck)
|
keeper := NewKeeper(ctx, cdc, keyStake, ck)
|
||||||
|
keeper.setPool(ctx, initialPool())
|
||||||
//params := paramsNoInflation()
|
keeper.setParams(ctx, defaultParams())
|
||||||
params := keeper.GetParams(ctx)
|
|
||||||
|
|
||||||
// fill all the addresses with some coins
|
// fill all the addresses with some coins
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
ck.AddCoins(ctx, addr, sdk.Coins{{params.BondDenom, initCoins}})
|
ck.AddCoins(ctx, addr, sdk.Coins{
|
||||||
|
{keeper.GetParams(ctx).BondDenom, initCoins},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx, accountMapper, keeper
|
return ctx, accountMapper, keeper
|
||||||
|
|
|
@ -6,38 +6,36 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
hrsPerYear = 8766 // as defined by a julian year of 365.25 days
|
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
|
||||||
precision = 1000000000
|
precision = 1000000000
|
||||||
)
|
)
|
||||||
|
|
||||||
var hrsPerYrRat = sdk.NewRat(hrsPerYear) // as defined by a julian year of 365.25 days
|
var hrsPerYrRat = sdk.NewRat(hrsPerYr) // as defined by a julian year of 365.25 days
|
||||||
|
|
||||||
// Tick - called at the end of every block
|
// Tick - called at the end of every block
|
||||||
func (k Keeper) Tick(ctx sdk.Context) (change []*abci.Validator, err error) {
|
func (k Keeper) Tick(ctx sdk.Context) (change []abci.Validator) {
|
||||||
|
|
||||||
// retrieve params
|
|
||||||
p := k.GetPool(ctx)
|
p := k.GetPool(ctx)
|
||||||
height := ctx.BlockHeight()
|
|
||||||
|
|
||||||
// Process Validator Provisions
|
// Process Validator Provisions
|
||||||
// XXX right now just process every 5 blocks, in new SDK make hourly
|
blockTime := ctx.BlockHeader().Time // XXX assuming in seconds, confirm
|
||||||
if p.InflationLastTime+5 <= height {
|
if p.InflationLastTime+blockTime >= 3600 {
|
||||||
p.InflationLastTime = height
|
p.InflationLastTime = blockTime
|
||||||
k.processProvisions(ctx)
|
p = k.processProvisions(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
newVals := k.GetValidators(ctx)
|
// save the params
|
||||||
|
k.setPool(ctx, p)
|
||||||
|
|
||||||
// XXX determine change from old validators, set to change
|
change = k.getAccUpdateValidators(ctx)
|
||||||
_ = newVals
|
|
||||||
return change, nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// process provisions for an hour period
|
// process provisions for an hour period
|
||||||
func (k Keeper) processProvisions(ctx sdk.Context) {
|
func (k Keeper) processProvisions(ctx sdk.Context) Pool {
|
||||||
|
|
||||||
pool := k.GetPool(ctx)
|
pool := k.GetPool(ctx)
|
||||||
pool.Inflation = k.nextInflation(ctx).Round(precision)
|
pool.Inflation = k.nextInflation(ctx)
|
||||||
|
|
||||||
// Because the validators hold a relative bonded share (`GlobalStakeShare`), when
|
// Because the validators hold a relative bonded share (`GlobalStakeShare`), when
|
||||||
// more bonded tokens are added proportionally to all validators the only term
|
// more bonded tokens are added proportionally to all validators the only term
|
||||||
|
@ -46,9 +44,7 @@ func (k Keeper) processProvisions(ctx sdk.Context) {
|
||||||
provisions := pool.Inflation.Mul(sdk.NewRat(pool.TotalSupply)).Quo(hrsPerYrRat).Evaluate()
|
provisions := pool.Inflation.Mul(sdk.NewRat(pool.TotalSupply)).Quo(hrsPerYrRat).Evaluate()
|
||||||
pool.BondedPool += provisions
|
pool.BondedPool += provisions
|
||||||
pool.TotalSupply += provisions
|
pool.TotalSupply += provisions
|
||||||
|
return pool
|
||||||
// save the params
|
|
||||||
k.setPool(ctx, pool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the next inflation rate for the hour
|
// get the next inflation rate for the hour
|
||||||
|
@ -75,5 +71,5 @@ func (k Keeper) nextInflation(ctx sdk.Context) (inflation sdk.Rat) {
|
||||||
inflation = params.InflationMin
|
inflation = params.InflationMin
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return inflation.Round(precision)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,116 +1,134 @@
|
||||||
package stake
|
package stake
|
||||||
|
|
||||||
//import (
|
import (
|
||||||
//"testing"
|
"testing"
|
||||||
|
|
||||||
//sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
//"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
//)
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
//func TestGetInflation(t *testing.T) {
|
func TestGetInflation(t *testing.T) {
|
||||||
//ctx, _, keeper := createTestInput(t, nil, false, 0)
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
//params := defaultParams()
|
pool := keeper.GetPool(ctx)
|
||||||
//keeper.setParams(ctx, params)
|
params := keeper.GetParams(ctx)
|
||||||
//gs := keeper.GetPool(ctx)
|
hrsPerYrRat := sdk.NewRat(hrsPerYr)
|
||||||
|
|
||||||
//// Governing Mechanism:
|
// Governing Mechanism:
|
||||||
//// bondedRatio = BondedPool / TotalSupply
|
// bondedRatio = BondedPool / TotalSupply
|
||||||
//// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
|
// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
|
||||||
|
|
||||||
//tests := []struct {
|
tests := []struct {
|
||||||
//setBondedPool, setTotalSupply int64
|
name string
|
||||||
//setInflation, expectedChange sdk.Rat
|
setBondedPool, setTotalSupply int64
|
||||||
//}{
|
setInflation, expectedChange sdk.Rat
|
||||||
//// with 0% bonded atom supply the inflation should increase by InflationRateChange
|
}{
|
||||||
//{0, 0, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYr)},
|
// with 0% bonded atom supply the inflation should increase by InflationRateChange
|
||||||
|
{"test 1", 0, 0, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYrRat).Round(precision)},
|
||||||
|
|
||||||
//// 100% bonded, starting at 20% inflation and being reduced
|
// 100% bonded, starting at 20% inflation and being reduced
|
||||||
//{1, 1, sdk.NewRat(20, 100), sdk.OneRat.Sub(sdk.OneRat.Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYr)},
|
// (1 - (1/0.67))*(0.13/8667)
|
||||||
|
{"test 2", 1, 1, sdk.NewRat(20, 100),
|
||||||
|
sdk.OneRat.Sub(sdk.OneRat.Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
|
||||||
|
|
||||||
//// 50% bonded, starting at 10% inflation and being increased
|
// 50% bonded, starting at 10% inflation and being increased
|
||||||
//{1, 2, sdk.NewRat(10, 100), sdk.OneRat.Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYr)},
|
{"test 3", 1, 2, sdk.NewRat(10, 100),
|
||||||
|
sdk.OneRat.Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
|
||||||
|
|
||||||
//// test 7% minimum stop (testing with 100% bonded)
|
// test 7% minimum stop (testing with 100% bonded)
|
||||||
//{1, 1, sdk.NewRat(7, 100), sdk.ZeroRat},
|
{"test 4", 1, 1, sdk.NewRat(7, 100), sdk.ZeroRat},
|
||||||
//{1, 1, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000)},
|
{"test 5", 1, 1, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
|
||||||
|
|
||||||
//// test 20% maximum stop (testing with 0% bonded)
|
// test 20% maximum stop (testing with 0% bonded)
|
||||||
//{0, 0, sdk.NewRat(20, 100), sdk.ZeroRat},
|
{"test 6", 0, 0, sdk.NewRat(20, 100), sdk.ZeroRat},
|
||||||
//{0, 0, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000)},
|
{"test 7", 0, 0, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000).Round(precision)},
|
||||||
|
|
||||||
//// perfect balance shouldn't change inflation
|
// perfect balance shouldn't change inflation
|
||||||
//{67, 100, sdk.NewRat(15, 100), sdk.ZeroRat},
|
{"test 8", 67, 100, sdk.NewRat(15, 100), sdk.ZeroRat},
|
||||||
//}
|
}
|
||||||
//for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
//gs.BondedPool, p.TotalSupply = tc.setBondedPool, tc.setTotalSupply
|
pool.BondedPool, pool.TotalSupply = tc.setBondedPool, tc.setTotalSupply
|
||||||
//gs.Inflation = tc.setInflation
|
pool.Inflation = tc.setInflation
|
||||||
|
keeper.setPool(ctx, pool)
|
||||||
|
|
||||||
//inflation := nextInflation(gs, params)
|
inflation := keeper.nextInflation(ctx)
|
||||||
//diffInflation := inflation.Sub(tc.setInflation)
|
diffInflation := inflation.Sub(tc.setInflation)
|
||||||
|
|
||||||
//assert.True(t, diffInflation.Equal(tc.expectedChange),
|
assert.True(t, diffInflation.Equal(tc.expectedChange),
|
||||||
//"%v, %v", diffInflation, tc.expectedChange)
|
"Name: %v\nDiff: %v\nExpected: %v\n", tc.name, diffInflation, tc.expectedChange)
|
||||||
//}
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
//func TestProcessProvisions(t *testing.T) {
|
func TestProcessProvisions(t *testing.T) {
|
||||||
//ctx, _, keeper := createTestInput(t, nil, false, 0)
|
ctx, _, keeper := createTestInput(t, false, 0)
|
||||||
//params := defaultParams()
|
params := defaultParams()
|
||||||
//keeper.setParams(ctx, params)
|
keeper.setParams(ctx, params)
|
||||||
//gs := keeper.GetPool(ctx)
|
pool := keeper.GetPool(ctx)
|
||||||
|
|
||||||
//// create some candidates some bonded, some unbonded
|
// create some candidates some bonded, some unbonded
|
||||||
//candidates := candidatesFromAddrsEmpty(addrs)
|
candidates := make([]Candidate, 10)
|
||||||
//for i, candidate := range candidates {
|
for i := 0; i < 10; i++ {
|
||||||
//if i < 5 {
|
c := Candidate{
|
||||||
//candidate.Status = Bonded
|
Status: Unbonded,
|
||||||
//}
|
PubKey: pks[i],
|
||||||
//mintedTokens := int64((i + 1) * 10000000)
|
Address: addrs[i],
|
||||||
//gs.TotalSupply += mintedTokens
|
Assets: sdk.NewRat(0),
|
||||||
//keeper.candidateAddTokens(ctx, candidate, mintedTokens)
|
Liabilities: sdk.NewRat(0),
|
||||||
//keeper.setCandidate(ctx, candidate)
|
}
|
||||||
//}
|
if i < 5 {
|
||||||
//var totalSupply int64 = 550000000
|
c.Status = Bonded
|
||||||
//var bondedShares int64 = 150000000
|
}
|
||||||
//var unbondedShares int64 = 400000000
|
mintedTokens := int64((i + 1) * 10000000)
|
||||||
|
pool.TotalSupply += mintedTokens
|
||||||
|
pool, c, _ = pool.candidateAddTokens(c, mintedTokens)
|
||||||
|
|
||||||
//// initial bonded ratio ~ 27%
|
keeper.setCandidate(ctx, c)
|
||||||
//assert.True(t, p.bondedRatio().Equal(sdk.NewRat(bondedShares, totalSupply)), "%v", p.bondedRatio())
|
candidates[i] = c
|
||||||
|
}
|
||||||
|
keeper.setPool(ctx, pool)
|
||||||
|
var totalSupply int64 = 550000000
|
||||||
|
var bondedShares int64 = 150000000
|
||||||
|
var unbondedShares int64 = 400000000
|
||||||
|
assert.Equal(t, totalSupply, pool.TotalSupply)
|
||||||
|
assert.Equal(t, bondedShares, pool.BondedPool)
|
||||||
|
assert.Equal(t, unbondedShares, pool.UnbondedPool)
|
||||||
|
|
||||||
//// Supplies
|
// initial bonded ratio ~ 27%
|
||||||
//assert.Equal(t, totalSupply, p.TotalSupply)
|
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(bondedShares, totalSupply)), "%v", pool.bondedRatio())
|
||||||
//assert.Equal(t, bondedShares, p.BondedPool)
|
|
||||||
//assert.Equal(t, unbondedShares, p.UnbondedPool)
|
|
||||||
|
|
||||||
//// test the value of candidate shares
|
// test the value of candidate shares
|
||||||
//assert.True(t, p.bondedShareExRate().Equal(sdk.OneRat), "%v", p.bondedShareExRate())
|
assert.True(t, pool.bondedShareExRate().Equal(sdk.OneRat), "%v", pool.bondedShareExRate())
|
||||||
|
|
||||||
//initialSupply := p.TotalSupply
|
initialSupply := pool.TotalSupply
|
||||||
//initialUnbonded := p.TotalSupply - p.BondedPool
|
initialUnbonded := pool.TotalSupply - pool.BondedPool
|
||||||
|
|
||||||
//// process the provisions a year
|
// process the provisions a year
|
||||||
//for hr := 0; hr < 8766; hr++ {
|
for hr := 0; hr < 8766; hr++ {
|
||||||
//expInflation := nextInflation(gs, params).Round(1000000000)
|
pool := keeper.GetPool(ctx)
|
||||||
//expProvisions := (expInflation.Mul(sdk.NewRat(gs.TotalSupply)).Quo(hrsPerYr)).Evaluate()
|
expInflation := keeper.nextInflation(ctx).Round(1000000000)
|
||||||
//startBondedPool := p.BondedPool
|
expProvisions := (expInflation.Mul(sdk.NewRat(pool.TotalSupply)).Quo(hrsPerYrRat)).Evaluate()
|
||||||
//startTotalSupply := p.TotalSupply
|
startBondedPool := pool.BondedPool
|
||||||
//processProvisions(ctx, keeper, p, params)
|
startTotalSupply := pool.TotalSupply
|
||||||
//assert.Equal(t, startBondedPool+expProvisions, p.BondedPool)
|
pool = keeper.processProvisions(ctx)
|
||||||
//assert.Equal(t, startTotalSupply+expProvisions, p.TotalSupply)
|
keeper.setPool(ctx, pool)
|
||||||
//}
|
//fmt.Printf("hr %v, startBondedPool %v, expProvisions %v, pool.BondedPool %v\n", hr, startBondedPool, expProvisions, pool.BondedPool)
|
||||||
//assert.NotEqual(t, initialSupply, p.TotalSupply)
|
require.Equal(t, startBondedPool+expProvisions, pool.BondedPool, "hr %v", hr)
|
||||||
//assert.Equal(t, initialUnbonded, p.UnbondedPool)
|
require.Equal(t, startTotalSupply+expProvisions, pool.TotalSupply)
|
||||||
////panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", p.TotalSupply, p.BondedPool, p.TotalSupply-gs.BondedPool))
|
}
|
||||||
|
pool = keeper.GetPool(ctx)
|
||||||
|
assert.NotEqual(t, initialSupply, pool.TotalSupply)
|
||||||
|
assert.Equal(t, initialUnbonded, pool.UnbondedPool)
|
||||||
|
//panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", p.TotalSupply, p.BondedPool, pool.TotalSupply-pool.BondedPool))
|
||||||
|
|
||||||
//// initial bonded ratio ~ 35% ~ 30% increase for bonded holders
|
// initial bonded ratio ~ from 27% to 40% increase for bonded holders ownership of total supply
|
||||||
//assert.True(t, p.bondedRatio().Equal(sdk.NewRat(105906511, 305906511)), "%v", p.bondedRatio())
|
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(271734723, 671734723)), "%v", pool.bondedRatio())
|
||||||
|
|
||||||
//// global supply
|
// global supply
|
||||||
//assert.Equal(t, int64(611813022), p.TotalSupply)
|
assert.Equal(t, int64(671734723), pool.TotalSupply)
|
||||||
//assert.Equal(t, int64(211813022), p.BondedPool)
|
assert.Equal(t, int64(271734723), pool.BondedPool)
|
||||||
//assert.Equal(t, unbondedShares, p.UnbondedPool)
|
assert.Equal(t, unbondedShares, pool.UnbondedPool)
|
||||||
|
|
||||||
//// test the value of candidate shares
|
// test the value of candidate shares
|
||||||
//assert.True(t, p.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(211813022)), "%v", p.bondedShareExRate())
|
assert.True(t, pool.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(271734723)), "%v", pool.bondedShareExRate())
|
||||||
|
|
||||||
//}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package stake
|
||||||
|
|
||||||
import (
|
import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,19 +18,6 @@ type Params struct {
|
||||||
BondDenom string `json:"bond_denom"` // bondable coin denomination
|
BondDenom string `json:"bond_denom"` // bondable coin denomination
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX do we want to allow for default params even or do we want to enforce that you
|
|
||||||
// need to be explicit about defining all params in genesis?
|
|
||||||
func defaultParams() Params {
|
|
||||||
return Params{
|
|
||||||
InflationRateChange: sdk.NewRat(13, 100),
|
|
||||||
InflationMax: sdk.NewRat(20, 100),
|
|
||||||
InflationMin: sdk.NewRat(7, 100),
|
|
||||||
GoalBonded: sdk.NewRat(67, 100),
|
|
||||||
MaxValidators: 100,
|
|
||||||
BondDenom: "fermion",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//_________________________________________________________________________
|
//_________________________________________________________________________
|
||||||
|
|
||||||
// Pool - dynamic parameters of the current state
|
// Pool - dynamic parameters of the current state
|
||||||
|
@ -42,42 +31,10 @@ type Pool struct {
|
||||||
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
|
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX define globalstate interface?
|
// GenesisState - all staking state that must be provided at genesis
|
||||||
|
type GenesisState struct {
|
||||||
func initialPool() Pool {
|
Pool Pool `json:"pool"`
|
||||||
return Pool{
|
Params Params `json:"params"`
|
||||||
TotalSupply: 0,
|
|
||||||
BondedShares: sdk.ZeroRat,
|
|
||||||
UnbondedShares: sdk.ZeroRat,
|
|
||||||
BondedPool: 0,
|
|
||||||
UnbondedPool: 0,
|
|
||||||
InflationLastTime: 0,
|
|
||||||
Inflation: sdk.NewRat(7, 100),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the bond ratio of the global state
|
|
||||||
func (p Pool) bondedRatio() sdk.Rat {
|
|
||||||
if p.TotalSupply > 0 {
|
|
||||||
return sdk.NewRat(p.BondedPool, p.TotalSupply)
|
|
||||||
}
|
|
||||||
return sdk.ZeroRat
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the exchange rate of bonded token per issued share
|
|
||||||
func (p Pool) bondedShareExRate() sdk.Rat {
|
|
||||||
if p.BondedShares.IsZero() {
|
|
||||||
return sdk.OneRat
|
|
||||||
}
|
|
||||||
return sdk.NewRat(p.BondedPool).Quo(p.BondedShares)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the exchange rate of unbonded tokens held in candidates per issued share
|
|
||||||
func (p Pool) unbondedShareExRate() sdk.Rat {
|
|
||||||
if p.UnbondedShares.IsZero() {
|
|
||||||
return sdk.OneRat
|
|
||||||
}
|
|
||||||
return sdk.NewRat(p.UnbondedPool).Quo(p.UnbondedShares)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//_______________________________________________________________________________________________________
|
//_______________________________________________________________________________________________________
|
||||||
|
@ -149,8 +106,9 @@ func (c Candidate) delegatorShareExRate() sdk.Rat {
|
||||||
// Should only be called when the Candidate qualifies as a validator.
|
// Should only be called when the Candidate qualifies as a validator.
|
||||||
func (c Candidate) validator() Validator {
|
func (c Candidate) validator() Validator {
|
||||||
return Validator{
|
return Validator{
|
||||||
Address: c.Address, // XXX !!!
|
Address: c.Address,
|
||||||
VotingPower: c.Assets,
|
PubKey: c.PubKey,
|
||||||
|
Power: c.Assets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,23 +119,35 @@ func (c Candidate) validator() Validator {
|
||||||
|
|
||||||
// Validator is one of the top Candidates
|
// Validator is one of the top Candidates
|
||||||
type Validator struct {
|
type Validator struct {
|
||||||
Address sdk.Address `json:"address"` // Address of validator
|
Address sdk.Address `json:"address"`
|
||||||
VotingPower sdk.Rat `json:"voting_power"` // Voting power if considered a validator
|
PubKey crypto.PubKey `json:"pub_key"`
|
||||||
|
Power sdk.Rat `json:"voting_power"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ABCIValidator - Get the validator from a bond value
|
// abci validator from stake validator type
|
||||||
/* TODO
|
func (v Validator) abciValidator(cdc *wire.Codec) abci.Validator {
|
||||||
func (v Validator) ABCIValidator() (*abci.Validator, error) {
|
pkBytes, err := cdc.MarshalBinary(v.PubKey)
|
||||||
pkBytes, err := wire.MarshalBinary(v.PubKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
panic(err)
|
||||||
}
|
}
|
||||||
return &abci.Validator{
|
return abci.Validator{
|
||||||
PubKey: pkBytes,
|
PubKey: pkBytes,
|
||||||
Power: v.VotingPower.Evaluate(),
|
Power: v.Power.Evaluate(),
|
||||||
}, nil
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// abci validator from stake validator type
|
||||||
|
// with zero power used for validator updates
|
||||||
|
func (v Validator) abciValidatorZero(cdc *wire.Codec) abci.Validator {
|
||||||
|
pkBytes, err := cdc.MarshalBinary(v.PubKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return abci.Validator{
|
||||||
|
PubKey: pkBytes,
|
||||||
|
Power: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
//_________________________________________________________________________
|
//_________________________________________________________________________
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue