Merge branch 'develop' into fedekunze/README
This commit is contained in:
commit
56c3ae4a7f
26
CHANGELOG.md
26
CHANGELOG.md
|
@ -1,27 +1,31 @@
|
|||
# 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
|
||||
|
||||
* [baseapp] `AddRoute` takes an `InitGenesis` function for per-module
|
||||
initialization
|
||||
* [basecoin] Remove cool/sketchy modules -> moved to new `democoin`
|
||||
* [basecoin] NewBasecoinApp takes a `map[string]dbm.DB` as temporary measure
|
||||
to allow mounting multiple stores with their own DB until they can share one
|
||||
* [staking] Renamed to `simplestake`
|
||||
* [x/staking] Renamed to `simplestake`
|
||||
* [builder] Functions don't take `passphrase` as argument
|
||||
* [server] GenAppState returns generated seed and address
|
||||
* [basecoind] `init` command outputs JSON of everything necessary for testnet
|
||||
* [basecoind] `basecoin.db -> data/basecoin.db`
|
||||
* [basecli] `data/keys.db -> keys/keys.db`
|
||||
* [cool] Mapper -> Keeper
|
||||
|
||||
FEATURES
|
||||
|
||||
* [types] `Coin` supports direct arithmetic operations
|
||||
* [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
|
||||
|
||||
IMPROVEMENTS
|
||||
|
@ -45,9 +49,9 @@ BREAKING CHANGES
|
|||
* [types] Replace tx.GetFeePayer with FeePayer(tx) - returns the first signer
|
||||
* [types] NewStdTx takes the Fee
|
||||
* [types] ParseAccount -> AccountDecoder; ErrTxParse -> ErrTxDecoder
|
||||
* [auth] AnteHandler deducts fees
|
||||
* [bank] Move some errors to `types`
|
||||
* [bank] Remove sequence and signature from Input
|
||||
* [x/auth] AnteHandler deducts fees
|
||||
* [x/bank] Move some errors to `types`
|
||||
* [x/bank] Remove sequence and signature from Input
|
||||
|
||||
FEATURES
|
||||
|
||||
|
@ -71,8 +75,8 @@ IMPROVEMENTS
|
|||
* [specs] Staking
|
||||
|
||||
BUG FIXES
|
||||
* [auth] Fix setting pubkey on new account
|
||||
* [auth] Require signatures to include the sequences
|
||||
* [x/auth] Fix setting pubkey on new account
|
||||
* [x/auth] Require signatures to include the sequences
|
||||
* [baseapp] Dont panic on nil handler
|
||||
* [basecoin] Check for empty bytes in account and tx
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@
|
|||
"json/scanner",
|
||||
"json/token"
|
||||
]
|
||||
revision = "f40e974e75af4e271d97ce0fc917af5898ae7bda"
|
||||
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -191,8 +191,8 @@
|
|||
".",
|
||||
"mem"
|
||||
]
|
||||
revision = "bb8f1927f2a9d3ab41c9340aa034f6b803f4359c"
|
||||
version = "v1.0.2"
|
||||
revision = "63644898a8da0bc22138abf860edaf5277b6102e"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/cast"
|
||||
|
@ -203,8 +203,8 @@
|
|||
[[projects]]
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = ["."]
|
||||
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
|
||||
version = "v0.0.1"
|
||||
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
|
||||
version = "v0.0.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -250,7 +250,7 @@
|
|||
"leveldb/table",
|
||||
"leveldb/util"
|
||||
]
|
||||
revision = "169b1b37be738edb2813dab48c97a549bcf99bb5"
|
||||
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/tendermint/abci"
|
||||
|
@ -341,8 +341,8 @@
|
|||
"version",
|
||||
"wire"
|
||||
]
|
||||
revision = "6f9956990c444d53f62f2a3905ed410cfe9afe77"
|
||||
version = "v0.17.1"
|
||||
revision = "a1dd329d72e78d4770e602359bad5b7b1e8b72a3"
|
||||
version = "v0.18.0-rc1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/tendermint/tmlibs"
|
||||
|
@ -359,8 +359,8 @@
|
|||
"pubsub",
|
||||
"pubsub/query"
|
||||
]
|
||||
revision = "24da7009c3d8c019b40ba4287495749e3160caca"
|
||||
version = "v0.7.1"
|
||||
revision = "2e24b64fc121dcdf1cabceab8dc2f7257675483c"
|
||||
version = "0.8.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -376,7 +376,7 @@
|
|||
"ripemd160",
|
||||
"salsa20/salsa"
|
||||
]
|
||||
revision = "88942b9c40a4c9d203b82b3731787b672d6e809b"
|
||||
revision = "b2aa35443fbc700ab74c586ae79b81c171851023"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -390,13 +390,13 @@
|
|||
"lex/httplex",
|
||||
"trace"
|
||||
]
|
||||
revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
|
||||
revision = "b3c676e531a6dc479fa1b35ac961c13f5e2b4d2e"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "13d03a9a82fba647c21a0ef8fba44a795d0f0835"
|
||||
revision = "1d206c9fa8975fb4cf00df1dc8bf3283dc24ba0e"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/text"
|
||||
|
@ -423,7 +423,7 @@
|
|||
branch = "master"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
revision = "ab0870e398d5dd054b868c0db1481ab029b9a9f2"
|
||||
revision = "35de2414665fc36f56b72d982c5af480d86de5ab"
|
||||
|
||||
[[projects]]
|
||||
name = "google.golang.org/grpc"
|
||||
|
@ -452,12 +452,12 @@
|
|||
[[projects]]
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
revision = "86f5ed62f8a0ee96bd888d2efdfd6d4fb100a4eb"
|
||||
version = "v2.2.0"
|
||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||
version = "v2.2.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "ed1f3f7f1728cd02945f90ca780e9bdc982573a36a5cc8d7e9f19fb40ba2ca19"
|
||||
inputs-digest = "67298e1f8058b85f082dbd32123f2779b11bda282616e595141dba41a8675c39"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -70,11 +70,11 @@
|
|||
name = "github.com/tendermint/iavl"
|
||||
|
||||
[[constraint]]
|
||||
version = "~0.17.1"
|
||||
version = "~0.18.0-rc1"
|
||||
name = "github.com/tendermint/tendermint"
|
||||
|
||||
[[constraint]]
|
||||
version = "~0.7.1"
|
||||
[[override]]
|
||||
version = "~0.8.1"
|
||||
name = "github.com/tendermint/tmlibs"
|
||||
|
||||
[prune]
|
||||
|
|
26
Makefile
26
Makefile
|
@ -2,7 +2,7 @@ PACKAGES=$(shell go list ./... | grep -v '/vendor/')
|
|||
COMMIT_HASH := $(shell git rev-parse --short HEAD)
|
||||
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}"
|
||||
|
||||
all: check_tools get_vendor_deps build test
|
||||
all: check_tools get_vendor_deps build build_examples test
|
||||
|
||||
########################################
|
||||
### CI
|
||||
|
@ -13,13 +13,16 @@ ci: get_tools get_vendor_deps build test_cover
|
|||
### Build
|
||||
|
||||
# This can be unified later, here for easy demos
|
||||
gaia:
|
||||
go build $(BUILD_FLAGS) -o build/gaiad ./examples/gaia/gaiad
|
||||
go build $(BUILD_FLAGS) -o build/gaiacli ./examples/gaia/gaiacli
|
||||
|
||||
build:
|
||||
@rm -rf $(shell pwd)/examples/basecoin/vendor/
|
||||
@rm -rf $(shell pwd)/examples/democoin/vendor/
|
||||
ifeq ($(OS),Windows_NT)
|
||||
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaiad
|
||||
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaiacli
|
||||
else
|
||||
go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaiad
|
||||
go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaiacli
|
||||
endif
|
||||
|
||||
build_examples:
|
||||
ifeq ($(OS),Windows_NT)
|
||||
go build $(BUILD_FLAGS) -o build/basecoind.exe ./examples/basecoin/cmd/basecoind
|
||||
go build $(BUILD_FLAGS) -o build/basecli.exe ./examples/basecoin/cmd/basecli
|
||||
|
@ -33,6 +36,10 @@ else
|
|||
endif
|
||||
|
||||
install:
|
||||
go install $(BUILD_FLAGS) ./cmd/gaiad
|
||||
go install $(BUILD_FLAGS) ./cmd/gaiacli
|
||||
|
||||
install_examples:
|
||||
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecoind
|
||||
go install $(BUILD_FLAGS) ./examples/basecoin/cmd/basecli
|
||||
go install $(BUILD_FLAGS) ./examples/democoin/cmd/democoind
|
||||
|
@ -84,12 +91,9 @@ test: test_unit # test_cli
|
|||
# go test -coverprofile=c.out && go tool cover -html=c.out
|
||||
|
||||
test_unit:
|
||||
@rm -rf examples/basecoin/vendor/
|
||||
@rm -rf examples/democoin/vendor/
|
||||
@go test $(PACKAGES)
|
||||
|
||||
test_cover:
|
||||
@rm -rf examples/basecoin/vendor/
|
||||
@bash tests/test_cover.sh
|
||||
|
||||
benchmark:
|
||||
|
@ -123,4 +127,4 @@ devdoc_update:
|
|||
# To avoid unintended conflicts with file names, always add to .PHONY
|
||||
# unless there is a reason not to.
|
||||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
||||
.PHONY: build dist check_tools get_tools get_vendor_deps draw_deps test test_unit test_tutorial benchmark devdoc_init devdoc devdoc_save devdoc_update
|
||||
.PHONY: build build_examples install install_examples dist check_tools get_tools get_vendor_deps draw_deps test test_unit test_tutorial benchmark devdoc_init devdoc devdoc_save devdoc_update
|
||||
|
|
|
@ -70,4 +70,4 @@ The key directories of the SDK are:
|
|||
|
||||
## 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
|
||||
panic(err)
|
||||
}
|
||||
err = app.Router().InitGenesis(app.deliverState.ctx, *genesisState)
|
||||
if err != nil {
|
||||
// TODO Return something intelligent
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// NOTE: we don't commit, but BeginBlock for block 1
|
||||
// starts from this deliverState
|
||||
|
@ -266,7 +261,7 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
|||
queryable, ok := app.cms.(sdk.Queryable)
|
||||
if !ok {
|
||||
msg := "application doesn't support queries"
|
||||
return sdk.ErrUnknownRequest(msg).Result().ToQuery()
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
}
|
||||
return queryable.Query(req)
|
||||
}
|
||||
|
|
|
@ -249,7 +249,7 @@ func TestDeliverTx(t *testing.T) {
|
|||
|
||||
counter += 1
|
||||
return sdk.Result{}
|
||||
}, nil)
|
||||
})
|
||||
|
||||
tx := testUpdatePowerTx{} // doesn't matter
|
||||
header := abci.Header{AppHash: []byte("apphash")}
|
||||
|
@ -284,7 +284,7 @@ func TestQuery(t *testing.T) {
|
|||
store := ctx.KVStore(capKey)
|
||||
store.Set(key, value)
|
||||
return sdk.Result{}
|
||||
}, nil)
|
||||
})
|
||||
|
||||
query := abci.RequestQuery{
|
||||
Path: "/main/key",
|
||||
|
@ -349,7 +349,7 @@ func TestValidatorChange(t *testing.T) {
|
|||
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
// TODO
|
||||
return sdk.Result{}
|
||||
}, nil)
|
||||
})
|
||||
|
||||
// Load latest state, which should be empty.
|
||||
err := app.LoadLatestVersion(capKey)
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package baseapp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -10,16 +8,14 @@ import (
|
|||
|
||||
// Router provides handlers for each transaction type.
|
||||
type Router interface {
|
||||
AddRoute(r string, h sdk.Handler, i sdk.InitGenesis) (rtr Router)
|
||||
AddRoute(r string, h sdk.Handler) (rtr Router)
|
||||
Route(path string) (h sdk.Handler)
|
||||
InitGenesis(ctx sdk.Context, data map[string]json.RawMessage) error
|
||||
}
|
||||
|
||||
// map a transaction type to a handler and an initgenesis function
|
||||
type route struct {
|
||||
r string
|
||||
h sdk.Handler
|
||||
i sdk.InitGenesis
|
||||
}
|
||||
|
||||
type router struct {
|
||||
|
@ -38,11 +34,11 @@ func NewRouter() *router {
|
|||
var isAlpha = regexp.MustCompile(`^[a-zA-Z]+$`).MatchString
|
||||
|
||||
// AddRoute - TODO add description
|
||||
func (rtr *router) AddRoute(r string, h sdk.Handler, i sdk.InitGenesis) Router {
|
||||
func (rtr *router) AddRoute(r string, h sdk.Handler) Router {
|
||||
if !isAlpha(r) {
|
||||
panic("route expressions can only contain alphanumeric characters")
|
||||
}
|
||||
rtr.routes = append(rtr.routes, route{r, h, i})
|
||||
rtr.routes = append(rtr.routes, route{r, h})
|
||||
|
||||
return rtr
|
||||
}
|
||||
|
@ -57,20 +53,3 @@ func (rtr *router) Route(path string) (h sdk.Handler) {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitGenesis - call `InitGenesis`, where specified, for all routes
|
||||
// Return the first error if any, otherwise nil
|
||||
func (rtr *router) InitGenesis(ctx sdk.Context, data map[string]json.RawMessage) error {
|
||||
for _, route := range rtr.routes {
|
||||
if route.i != nil {
|
||||
encoded, found := data[route.r]
|
||||
if !found {
|
||||
return sdk.ErrGenesisParse(fmt.Sprintf("Expected module genesis information for module %s but it was not present", route.r))
|
||||
}
|
||||
if err := route.i(ctx, encoded); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
|
@ -17,9 +16,9 @@ import (
|
|||
)
|
||||
|
||||
// Broadcast the transaction bytes to Tendermint
|
||||
func BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
func (ctx CoreContext) BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
|
||||
node, err := client.GetNode()
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -43,17 +42,17 @@ func BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
|
|||
}
|
||||
|
||||
// Query from Tendermint with the provided key and storename
|
||||
func Query(key cmn.HexBytes, storeName string) (res []byte, err error) {
|
||||
func (ctx CoreContext) Query(key cmn.HexBytes, storeName string) (res []byte, err error) {
|
||||
|
||||
path := fmt.Sprintf("/%s/key", storeName)
|
||||
node, err := client.GetNode()
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
opts := rpcclient.ABCIQueryOptions{
|
||||
Height: viper.GetInt64(client.FlagHeight),
|
||||
Trusted: viper.GetBool(client.FlagTrustNode),
|
||||
Height: ctx.Height,
|
||||
Trusted: ctx.TrustNode,
|
||||
}
|
||||
result, err := node.ABCIQueryWithOptions(path, key, opts)
|
||||
if err != nil {
|
||||
|
@ -67,16 +66,16 @@ func Query(key cmn.HexBytes, storeName string) (res []byte, err error) {
|
|||
}
|
||||
|
||||
// Get the from address from the name flag
|
||||
func GetFromAddress() (from sdk.Address, err error) {
|
||||
func (ctx CoreContext) GetFromAddress() (from sdk.Address, err error) {
|
||||
|
||||
keybase, err := keys.GetKeyBase()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := viper.GetString(client.FlagName)
|
||||
name := ctx.FromAddressName
|
||||
if name == "" {
|
||||
return nil, errors.Errorf("must provide a name using --name")
|
||||
return nil, errors.Errorf("must provide a from address name")
|
||||
}
|
||||
|
||||
info, err := keybase.Get(name)
|
||||
|
@ -88,11 +87,11 @@ func GetFromAddress() (from sdk.Address, err error) {
|
|||
}
|
||||
|
||||
// sign and build the transaction from the msg
|
||||
func SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *wire.Codec) ([]byte, error) {
|
||||
func (ctx CoreContext) SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *wire.Codec) ([]byte, error) {
|
||||
|
||||
// build the Sign Messsage from the Standard Message
|
||||
chainID := viper.GetString(client.FlagChainID)
|
||||
sequence := int64(viper.GetInt(client.FlagSequence))
|
||||
chainID := ctx.ChainID
|
||||
sequence := ctx.Sequence
|
||||
signMsg := sdk.StdSignMsg{
|
||||
ChainID: chainID,
|
||||
Sequences: []int64{sequence},
|
||||
|
@ -114,7 +113,7 @@ func SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *wire.Codec) ([]byte
|
|||
sigs := []sdk.StdSignature{{
|
||||
PubKey: pubkey,
|
||||
Signature: sig,
|
||||
Sequence: viper.GetInt64(client.FlagSequence),
|
||||
Sequence: sequence,
|
||||
}}
|
||||
|
||||
// marshal bytes
|
||||
|
@ -124,23 +123,31 @@ func SignAndBuild(name, passphrase string, msg sdk.Msg, cdc *wire.Codec) ([]byte
|
|||
}
|
||||
|
||||
// sign and build the transaction from the msg
|
||||
func SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
passphrase, err := GetPassphraseFromStdin(name)
|
||||
func (ctx CoreContext) SignBuildBroadcast(name string, msg sdk.Msg, cdc *wire.Codec) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
passphrase, err := ctx.GetPassphraseFromStdin(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txBytes, err := SignAndBuild(name, passphrase, msg, cdc)
|
||||
txBytes, err := ctx.SignAndBuild(name, passphrase, msg, cdc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return BroadcastTx(txBytes)
|
||||
return ctx.BroadcastTx(txBytes)
|
||||
}
|
||||
|
||||
// get passphrase from std input
|
||||
func GetPassphraseFromStdin(name string) (pass string, err error) {
|
||||
func (ctx CoreContext) GetPassphraseFromStdin(name string) (pass string, err error) {
|
||||
buf := client.BufferStdin()
|
||||
prompt := fmt.Sprintf("Password to sign with '%s':", name)
|
||||
return client.GetPassword(prompt, buf)
|
||||
}
|
||||
|
||||
// GetNode prepares a simple rpc.Client
|
||||
func (ctx CoreContext) GetNode() (rpcclient.Client, error) {
|
||||
if ctx.Client == nil {
|
||||
return nil, errors.New("Must define node URI")
|
||||
}
|
||||
return ctx.Client, nil
|
||||
}
|
|
@ -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) {
|
||||
|
||||
time.Sleep(time.Second * 2) // TODO: LOL -> wait for blocks
|
||||
waitForHeight(2)
|
||||
|
||||
var resultBlock ctypes.ResultBlock
|
||||
|
||||
|
@ -222,8 +222,7 @@ func TestCoinSend(t *testing.T) {
|
|||
|
||||
// create TX
|
||||
receiveAddr, resultTx := doSend(t, port, seed)
|
||||
|
||||
time.Sleep(time.Second * 2) // T
|
||||
waitForHeight(resultTx.Height + 1)
|
||||
|
||||
// check if tx was commited
|
||||
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||
|
@ -258,7 +257,7 @@ func TestIBCTransfer(t *testing.T) {
|
|||
// create TX
|
||||
resultTx := doIBCTransfer(t, port, seed)
|
||||
|
||||
time.Sleep(time.Second * 2) // T
|
||||
waitForHeight(resultTx.Height + 1)
|
||||
|
||||
// check if tx was commited
|
||||
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||
|
@ -296,7 +295,7 @@ func TestTxs(t *testing.T) {
|
|||
// create TX
|
||||
_, resultTx := doSend(t, port, seed)
|
||||
|
||||
time.Sleep(time.Second * 2) // TO
|
||||
waitForHeight(resultTx.Height + 1)
|
||||
|
||||
// check if tx is findable
|
||||
res, body := request(t, port, "GET", fmt.Sprintf("/txs/%s", resultTx.Hash), nil)
|
||||
|
@ -392,7 +391,7 @@ func startTMAndLCD() (*nm.Node, net.Listener, error) {
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * 2)
|
||||
waitForStart()
|
||||
|
||||
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)
|
||||
|
||||
output, err := ioutil.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
require.Nil(t, err)
|
||||
|
||||
return res, string(output)
|
||||
|
@ -461,8 +461,6 @@ func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctype
|
|||
acc := auth.BaseAccount{}
|
||||
err = json.Unmarshal([]byte(body), &acc)
|
||||
require.Nil(t, err)
|
||||
fmt.Println("BODY", body)
|
||||
fmt.Println("ACC", acc)
|
||||
sequence := acc.Sequence
|
||||
|
||||
// send
|
||||
|
@ -490,8 +488,6 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
|
|||
acc := auth.BaseAccount{}
|
||||
err = json.Unmarshal([]byte(body), &acc)
|
||||
require.Nil(t, err)
|
||||
fmt.Println("BODY", body)
|
||||
fmt.Println("ACC", acc)
|
||||
sequence := acc.Sequence
|
||||
|
||||
// send
|
||||
|
@ -504,3 +500,72 @@ func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroad
|
|||
|
||||
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/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -31,7 +32,8 @@ func blockCommand() *cobra.Command {
|
|||
|
||||
func getBlock(height *int64) ([]byte, error) {
|
||||
// get the node
|
||||
node, err := client.GetNode()
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
node, err := ctx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -55,7 +57,7 @@ func getBlock(height *int64) ([]byte, error) {
|
|||
}
|
||||
|
||||
func GetChainHeight() (int64, error) {
|
||||
node, err := client.GetNode()
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
wire "github.com/tendermint/go-wire"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
)
|
||||
|
||||
|
@ -25,7 +26,7 @@ func statusCommand() *cobra.Command {
|
|||
|
||||
func getNodeStatus() (*ctypes.ResultStatus, error) {
|
||||
// get the node
|
||||
node, err := client.GetNode()
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
if err != nil {
|
||||
return &ctypes.ResultStatus{}, err
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
)
|
||||
|
||||
func validatorCommand() *cobra.Command {
|
||||
|
@ -26,7 +27,7 @@ func validatorCommand() *cobra.Command {
|
|||
|
||||
func GetValidators(height *int64) ([]byte, error) {
|
||||
// get the node
|
||||
node, err := client.GetNode()
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
)
|
||||
|
||||
type BroadcastTxBody struct {
|
||||
|
@ -22,7 +22,7 @@ func BroadcastTxRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
res, err := builder.BroadcastTx([]byte(m.TxBytes))
|
||||
res, err := context.NewCoreContextFromViper().BroadcastTx([]byte(m.TxBytes))
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
@ -39,7 +40,7 @@ func (c commander) queryTx(hashHexStr string, trustNode bool) ([]byte, error) {
|
|||
}
|
||||
|
||||
// get the node
|
||||
node, err := client.GetNode()
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
||||
|
@ -43,7 +44,7 @@ func (c commander) searchTx(tags []string) ([]byte, error) {
|
|||
query := strings.Join(tags, " AND ")
|
||||
|
||||
// get the node
|
||||
node, err := client.GetNode()
|
||||
node, err := context.NewCoreContextFromViper().GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@ const (
|
|||
flagFee = "fee"
|
||||
)
|
||||
|
||||
// gaiacliCmd is the entry point for this binary
|
||||
// rootCmd is the entry point for this binary
|
||||
var (
|
||||
gaiacliCmd = &cobra.Command{
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "gaiacli",
|
||||
Short: "Gaia light-client",
|
||||
}
|
||||
|
@ -54,16 +54,16 @@ func main() {
|
|||
cobra.EnableCommandSorting = false
|
||||
|
||||
// generic client commands
|
||||
AddClientCommands(gaiacliCmd)
|
||||
AddClientCommands(rootCmd)
|
||||
// query commands (custom to binary)
|
||||
gaiacliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
GetCommands(getAccountCmd)...)
|
||||
// post tx commands (custom to binary)
|
||||
gaiacliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
PostCommands(postSendCommand())...)
|
||||
|
||||
// add proxy, version and key info
|
||||
gaiacliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
lineBreak,
|
||||
serveCommand(),
|
||||
KeyCommands(),
|
||||
|
@ -72,6 +72,6 @@ func main() {
|
|||
)
|
||||
|
||||
// prepare and add flags
|
||||
executor := cli.PrepareBaseCmd(gaiacliCmd, "GA", os.ExpandEnv("$HOME/.gaiacli"))
|
||||
executor := cli.PrepareBaseCmd(rootCmd, "GA", os.ExpandEnv("$HOME/.gaiacli"))
|
||||
executor.Execute()
|
||||
}
|
|
@ -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)
|
||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(coinKeeper), nil).
|
||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper), nil).
|
||||
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper), nil)
|
||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
||||
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
|
||||
|
||||
// initialize BaseApp
|
||||
app.SetTxDecoder(app.txDecoder)
|
||||
|
|
|
@ -36,6 +36,7 @@ var (
|
|||
addr4 = priv4.PubKey().Address()
|
||||
coins = sdk.Coins{{"foocoin", 10}}
|
||||
halfCoins = sdk.Coins{{"foocoin", 5}}
|
||||
manyCoins = sdk.Coins{{"foocoin", 1}, {"barcoin", 1}}
|
||||
fee = sdk.StdFee{
|
||||
sdk.Coins{{"foocoin", 0}},
|
||||
0,
|
||||
|
@ -73,6 +74,15 @@ var (
|
|||
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) {
|
||||
|
@ -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) {
|
||||
logger, dbs := loggerAndDBs()
|
||||
bapp := NewBasecoinApp(logger, dbs)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -24,18 +23,14 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
||||
)
|
||||
|
||||
// gaiacliCmd is the entry point for this binary
|
||||
// rootCmd is the entry point for this binary
|
||||
var (
|
||||
basecliCmd = &cobra.Command{
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "basecli",
|
||||
Short: "Basecoin light-client",
|
||||
}
|
||||
)
|
||||
|
||||
func todoNotImplemented(_ *cobra.Command, _ []string) error {
|
||||
return errors.New("TODO: Command not yet implemented")
|
||||
}
|
||||
|
||||
func main() {
|
||||
// disable sorting
|
||||
cobra.EnableCommandSorting = false
|
||||
|
@ -48,36 +43,36 @@ func main() {
|
|||
// with the cdc
|
||||
|
||||
// add standard rpc, and tx commands
|
||||
rpc.AddCommands(basecliCmd)
|
||||
basecliCmd.AddCommand(client.LineBreak)
|
||||
tx.AddCommands(basecliCmd, cdc)
|
||||
basecliCmd.AddCommand(client.LineBreak)
|
||||
rpc.AddCommands(rootCmd)
|
||||
rootCmd.AddCommand(client.LineBreak)
|
||||
tx.AddCommands(rootCmd, cdc)
|
||||
rootCmd.AddCommand(client.LineBreak)
|
||||
|
||||
// add query/post commands (custom to binary)
|
||||
basecliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
client.GetCommands(
|
||||
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
|
||||
)...)
|
||||
basecliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
bankcmd.SendTxCmd(cdc),
|
||||
)...)
|
||||
basecliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCTransferCmd(cdc),
|
||||
)...)
|
||||
basecliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCRelayCmd(cdc),
|
||||
simplestakingcmd.BondTxCmd(cdc),
|
||||
)...)
|
||||
basecliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
simplestakingcmd.UnbondTxCmd(cdc),
|
||||
)...)
|
||||
|
||||
// add proxy, version and key info
|
||||
basecliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
client.LineBreak,
|
||||
lcd.ServeCommand(cdc),
|
||||
keys.Commands(),
|
||||
|
@ -86,6 +81,6 @@ func main() {
|
|||
)
|
||||
|
||||
// prepare and add flags
|
||||
executor := cli.PrepareMainCmd(basecliCmd, "BC", os.ExpandEnv("$HOME/.basecli"))
|
||||
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.basecli"))
|
||||
executor.Execute()
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
|
@ -10,58 +8,38 @@ import (
|
|||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
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"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
)
|
||||
|
||||
// basecoindCmd is the entry point for this binary
|
||||
// rootCmd is the entry point for this binary
|
||||
var (
|
||||
basecoindCmd = &cobra.Command{
|
||||
Use: "gaiad",
|
||||
Short: "Gaia Daemon (server)",
|
||||
context = server.NewDefaultContext()
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "basecoind",
|
||||
Short: "Basecoin Daemon (server)",
|
||||
PersistentPreRunE: server.PersistentPreRunEFn(context),
|
||||
}
|
||||
)
|
||||
|
||||
// defaultOptions sets up the app_options for the
|
||||
// default genesis file
|
||||
func defaultOptions(args []string) (json.RawMessage, 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) {
|
||||
dbMain, err := dbm.NewGoLevelDB("basecoin", filepath.Join(rootDir, "data"))
|
||||
dataDir := filepath.Join(rootDir, "data")
|
||||
dbMain, err := dbm.NewGoLevelDB("basecoin", dataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbAcc, err := dbm.NewGoLevelDB("basecoin-acc", filepath.Join(rootDir, "data"))
|
||||
dbAcc, err := dbm.NewGoLevelDB("basecoin-acc", dataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbIBC, err := dbm.NewGoLevelDB("basecoin-ibc", filepath.Join(rootDir, "data"))
|
||||
dbIBC, err := dbm.NewGoLevelDB("basecoin-ibc", dataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbStaking, err := dbm.NewGoLevelDB("basecoin-staking", filepath.Join(rootDir, "data"))
|
||||
dbStaking, err := dbm.NewGoLevelDB("basecoin-staking", dataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -76,21 +54,10 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
|||
}
|
||||
|
||||
func main() {
|
||||
// TODO: set logger through CLI
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
|
||||
With("module", "main")
|
||||
|
||||
basecoindCmd.AddCommand(
|
||||
server.InitCmd(defaultOptions, logger),
|
||||
server.StartCmd(generateApp, logger),
|
||||
server.UnsafeResetAllCmd(logger),
|
||||
server.ShowNodeIdCmd(logger),
|
||||
server.ShowValidatorCmd(logger),
|
||||
version.VersionCmd,
|
||||
)
|
||||
server.AddCommands(rootCmd, server.DefaultGenAppState, generateApp, context)
|
||||
|
||||
// prepare and add flags
|
||||
rootDir := os.ExpandEnv("$HOME/.basecoind")
|
||||
executor := cli.PrepareBaseCmd(basecoindCmd, "BC", rootDir)
|
||||
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
|
||||
executor.Execute()
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
|
|||
return &GenesisAccount{
|
||||
Name: aa.Name,
|
||||
Address: aa.Address,
|
||||
Coins: aa.Coins,
|
||||
Coins: aa.Coins.Sort(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
|
|||
func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) {
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: ga.Address,
|
||||
Coins: ga.Coins,
|
||||
Coins: ga.Coins.Sort(),
|
||||
}
|
||||
return &AppAccount{
|
||||
BaseAccount: baseAcc,
|
||||
|
|
|
@ -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/x/cool"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/sketchy"
|
||||
)
|
||||
|
||||
|
@ -34,6 +35,7 @@ type DemocoinApp struct {
|
|||
// keys to access the substores
|
||||
capKeyMainStore *sdk.KVStoreKey
|
||||
capKeyAccountStore *sdk.KVStoreKey
|
||||
capKeyPowStore *sdk.KVStoreKey
|
||||
capKeyIBCStore *sdk.KVStoreKey
|
||||
capKeyStakingStore *sdk.KVStoreKey
|
||||
|
||||
|
@ -48,6 +50,7 @@ func NewDemocoinApp(logger log.Logger, dbs map[string]dbm.DB) *DemocoinApp {
|
|||
cdc: MakeCodec(),
|
||||
capKeyMainStore: sdk.NewKVStoreKey("main"),
|
||||
capKeyAccountStore: sdk.NewKVStoreKey("acc"),
|
||||
capKeyPowStore: sdk.NewKVStoreKey("pow"),
|
||||
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
|
||||
capKeyStakingStore: sdk.NewKVStoreKey("stake"),
|
||||
}
|
||||
|
@ -61,20 +64,23 @@ func NewDemocoinApp(logger log.Logger, dbs map[string]dbm.DB) *DemocoinApp {
|
|||
// add handlers
|
||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
||||
coolKeeper := cool.NewKeeper(app.capKeyMainStore, coinKeeper)
|
||||
powKeeper := pow.NewKeeper(app.capKeyPowStore, pow.NewPowConfig("pow", int64(1)), coinKeeper)
|
||||
ibcMapper := ibc.NewIBCMapper(app.cdc, app.capKeyIBCStore)
|
||||
stakeKeeper := simplestake.NewKeeper(app.capKeyStakingStore, coinKeeper)
|
||||
app.Router().
|
||||
AddRoute("bank", bank.NewHandler(coinKeeper), nil).
|
||||
AddRoute("cool", cool.NewHandler(coolKeeper), coolKeeper.InitGenesis).
|
||||
AddRoute("sketchy", sketchy.NewHandler(), nil).
|
||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper), nil).
|
||||
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper), nil)
|
||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||
AddRoute("cool", cool.NewHandler(coolKeeper)).
|
||||
AddRoute("pow", powKeeper.Handler).
|
||||
AddRoute("sketchy", sketchy.NewHandler()).
|
||||
AddRoute("ibc", ibc.NewHandler(ibcMapper, coinKeeper)).
|
||||
AddRoute("simplestake", simplestake.NewHandler(stakeKeeper))
|
||||
|
||||
// initialize BaseApp
|
||||
app.SetTxDecoder(app.txDecoder)
|
||||
app.SetInitChainer(app.initChainer)
|
||||
app.SetInitChainer(app.initChainerFn(coolKeeper, powKeeper))
|
||||
app.MountStoreWithDB(app.capKeyMainStore, sdk.StoreTypeIAVL, dbs["main"])
|
||||
app.MountStoreWithDB(app.capKeyAccountStore, sdk.StoreTypeIAVL, dbs["acc"])
|
||||
app.MountStoreWithDB(app.capKeyPowStore, sdk.StoreTypeIAVL, dbs["pow"])
|
||||
app.MountStoreWithDB(app.capKeyIBCStore, sdk.StoreTypeIAVL, dbs["ibc"])
|
||||
app.MountStoreWithDB(app.capKeyStakingStore, sdk.StoreTypeIAVL, dbs["staking"])
|
||||
// NOTE: Broken until #532 lands
|
||||
|
@ -95,16 +101,18 @@ func MakeCodec() *wire.Codec {
|
|||
const msgTypeIssue = 0x2
|
||||
const msgTypeQuiz = 0x3
|
||||
const msgTypeSetTrend = 0x4
|
||||
const msgTypeIBCTransferMsg = 0x5
|
||||
const msgTypeIBCReceiveMsg = 0x6
|
||||
const msgTypeBondMsg = 0x7
|
||||
const msgTypeUnbondMsg = 0x8
|
||||
const msgTypeMine = 0x5
|
||||
const msgTypeIBCTransferMsg = 0x6
|
||||
const msgTypeIBCReceiveMsg = 0x7
|
||||
const msgTypeBondMsg = 0x8
|
||||
const msgTypeUnbondMsg = 0x9
|
||||
var _ = oldwire.RegisterInterface(
|
||||
struct{ sdk.Msg }{},
|
||||
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
|
||||
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
|
||||
oldwire.ConcreteType{cool.QuizMsg{}, msgTypeQuiz},
|
||||
oldwire.ConcreteType{cool.SetTrendMsg{}, msgTypeSetTrend},
|
||||
oldwire.ConcreteType{pow.MineMsg{}, msgTypeMine},
|
||||
oldwire.ConcreteType{ibc.IBCTransferMsg{}, msgTypeIBCTransferMsg},
|
||||
oldwire.ConcreteType{ibc.IBCReceiveMsg{}, msgTypeIBCReceiveMsg},
|
||||
oldwire.ConcreteType{simplestake.BondMsg{}, msgTypeBondMsg},
|
||||
|
@ -143,23 +151,39 @@ func (app *DemocoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
|
|||
}
|
||||
|
||||
// custom logic for democoin initialization
|
||||
func (app *DemocoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
stateJSON := req.AppStateBytes
|
||||
func (app *DemocoinApp) initChainerFn(coolKeeper cool.Keeper, powKeeper pow.Keeper) sdk.InitChainer {
|
||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
stateJSON := req.AppStateBytes
|
||||
|
||||
genesisState := new(types.GenesisState)
|
||||
err := json.Unmarshal(stateJSON, genesisState)
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
}
|
||||
genesisState := new(types.GenesisState)
|
||||
err := json.Unmarshal(stateJSON, genesisState)
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
}
|
||||
|
||||
for _, gacc := range genesisState.Accounts {
|
||||
acc, err := gacc.ToAppAccount()
|
||||
for _, gacc := range genesisState.Accounts {
|
||||
acc, err := gacc.ToAppAccount()
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
}
|
||||
app.accountMapper.SetAccount(ctx, acc)
|
||||
}
|
||||
|
||||
// Application specific genesis handling
|
||||
err = coolKeeper.InitGenesis(ctx, genesisState.CoolGenesis)
|
||||
if err != nil {
|
||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||
}
|
||||
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/x/cool"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
|
@ -71,6 +72,7 @@ func loggerAndDBs() (log.Logger, map[string]dbm.DB) {
|
|||
dbs := map[string]dbm.DB{
|
||||
"main": dbm.NewMemDB(),
|
||||
"acc": dbm.NewMemDB(),
|
||||
"pow": dbm.NewMemDB(),
|
||||
"ibc": dbm.NewMemDB(),
|
||||
"staking": dbm.NewMemDB(),
|
||||
}
|
||||
|
@ -238,6 +240,58 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
|||
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||
}
|
||||
|
||||
func TestMineMsg(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
// Construct genesis state
|
||||
// Construct some genesis bytes to reflect democoin/types/AppAccount
|
||||
coins := sdk.Coins{}
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: addr1,
|
||||
Coins: coins,
|
||||
}
|
||||
acc1 := &types.AppAccount{baseAcc, "foobart"}
|
||||
|
||||
// Construct genesis state
|
||||
genesisState := map[string]interface{}{
|
||||
"accounts": []*types.GenesisAccount{
|
||||
types.NewGenesisAccount(acc1),
|
||||
},
|
||||
"cool": map[string]string{
|
||||
"trend": "ice-cold",
|
||||
},
|
||||
"pow": map[string]uint64{
|
||||
"difficulty": 1,
|
||||
"count": 0,
|
||||
},
|
||||
}
|
||||
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
||||
require.Nil(t, err)
|
||||
|
||||
// Initialize the chain (nil)
|
||||
vals := []abci.Validator{}
|
||||
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
|
||||
bapp.Commit()
|
||||
|
||||
// A checkTx context (true)
|
||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||
assert.Equal(t, acc1, res1)
|
||||
|
||||
// Mine and check for reward
|
||||
mineMsg1 := pow.GenerateMineMsg(addr1, 1, 2)
|
||||
SignCheckDeliver(t, bapp, mineMsg1, 0, true)
|
||||
CheckBalance(t, bapp, "1pow")
|
||||
// Mine again and check for reward
|
||||
mineMsg2 := pow.GenerateMineMsg(addr1, 2, 3)
|
||||
SignCheckDeliver(t, bapp, mineMsg2, 1, true)
|
||||
CheckBalance(t, bapp, "2pow")
|
||||
// Mine again - should be invalid
|
||||
SignCheckDeliver(t, bapp, mineMsg2, 1, false)
|
||||
CheckBalance(t, bapp, "2pow")
|
||||
|
||||
}
|
||||
|
||||
func TestQuizMsg(t *testing.T) {
|
||||
bapp := newDemocoinApp()
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -22,20 +21,18 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
|
||||
"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 (
|
||||
democliCmd = &cobra.Command{
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "democli",
|
||||
Short: "Democoin light-client",
|
||||
}
|
||||
)
|
||||
|
||||
func todoNotImplemented(_ *cobra.Command, _ []string) error {
|
||||
return errors.New("TODO: Command not yet implemented")
|
||||
}
|
||||
|
||||
func main() {
|
||||
// disable sorting
|
||||
cobra.EnableCommandSorting = false
|
||||
|
@ -48,36 +45,44 @@ func main() {
|
|||
// with the cdc
|
||||
|
||||
// add standard rpc, and tx commands
|
||||
rpc.AddCommands(democliCmd)
|
||||
democliCmd.AddCommand(client.LineBreak)
|
||||
tx.AddCommands(democliCmd, cdc)
|
||||
democliCmd.AddCommand(client.LineBreak)
|
||||
rpc.AddCommands(rootCmd)
|
||||
rootCmd.AddCommand(client.LineBreak)
|
||||
tx.AddCommands(rootCmd, cdc)
|
||||
rootCmd.AddCommand(client.LineBreak)
|
||||
|
||||
// add query/post commands (custom to binary)
|
||||
democliCmd.AddCommand(
|
||||
// start with commands common to basecoin
|
||||
rootCmd.AddCommand(
|
||||
client.GetCommands(
|
||||
authcmd.GetAccountCmd("main", cdc, types.GetAccountDecoder(cdc)),
|
||||
)...)
|
||||
democliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
bankcmd.SendTxCmd(cdc),
|
||||
)...)
|
||||
democliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCTransferCmd(cdc),
|
||||
)...)
|
||||
democliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
ibccmd.IBCRelayCmd(cdc),
|
||||
simplestakingcmd.BondTxCmd(cdc),
|
||||
)...)
|
||||
democliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
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
|
||||
democliCmd.AddCommand(
|
||||
rootCmd.AddCommand(
|
||||
client.LineBreak,
|
||||
lcd.ServeCommand(cdc),
|
||||
keys.Commands(),
|
||||
|
@ -86,6 +91,6 @@ func main() {
|
|||
)
|
||||
|
||||
// prepare and add flags
|
||||
executor := cli.PrepareMainCmd(democliCmd, "BC", os.ExpandEnv("$HOME/.democli"))
|
||||
executor := cli.PrepareMainCmd(rootCmd, "BC", os.ExpandEnv("$HOME/.democli"))
|
||||
executor.Execute()
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
|
@ -10,45 +9,41 @@ import (
|
|||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/app"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
"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 (
|
||||
democoindCmd = &cobra.Command{
|
||||
Use: "democoind",
|
||||
Short: "Gaia Daemon (server)",
|
||||
context = server.NewDefaultContext()
|
||||
rootCmd = &cobra.Command{
|
||||
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
|
||||
func defaultOptions(args []string) (json.RawMessage, string, cmn.HexBytes, error) {
|
||||
addr, secret, err := server.GenerateCoinKey()
|
||||
func defaultAppState(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
|
||||
baseJSON, err := server.DefaultGenAppState(args, addr, coinDenom)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println("Secret phrase to access coins:")
|
||||
fmt.Println(secret)
|
||||
|
||||
opts := fmt.Sprintf(`{
|
||||
"accounts": [{
|
||||
"address": "%s",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "mycoin",
|
||||
"amount": 9007199254740992
|
||||
}
|
||||
]
|
||||
}]
|
||||
}`, addr)
|
||||
return json.RawMessage(opts), "", nil, nil
|
||||
var jsonMap map[string]json.RawMessage
|
||||
err = json.Unmarshal(baseJSON, &jsonMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonMap["cool"] = json.RawMessage(`{
|
||||
"trend": "ice-cold"
|
||||
}`)
|
||||
bz, err := json.Marshal(jsonMap)
|
||||
return json.RawMessage(bz), err
|
||||
}
|
||||
|
||||
func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
||||
|
@ -60,6 +55,10 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbPow, err := dbm.NewGoLevelDB("democoin-pow", filepath.Join(rootDir, "data"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbIBC, err := dbm.NewGoLevelDB("democoin-ibc", filepath.Join(rootDir, "data"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -71,6 +70,7 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
|||
dbs := map[string]dbm.DB{
|
||||
"main": dbMain,
|
||||
"acc": dbAcc,
|
||||
"pow": dbPow,
|
||||
"ibc": dbIBC,
|
||||
"staking": dbStaking,
|
||||
}
|
||||
|
@ -79,21 +79,10 @@ func generateApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
|||
}
|
||||
|
||||
func main() {
|
||||
// TODO: set logger through CLI
|
||||
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,
|
||||
)
|
||||
server.AddCommands(rootCmd, defaultAppState, generateApp, context)
|
||||
|
||||
// prepare and add flags
|
||||
rootDir := os.ExpandEnv("$HOME/.democoind")
|
||||
executor := cli.PrepareBaseCmd(democoindCmd, "BC", rootDir)
|
||||
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
|
||||
executor.Execute()
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/cool"
|
||||
"github.com/cosmos/cosmos-sdk/examples/democoin/x/pow"
|
||||
)
|
||||
|
||||
var _ sdk.Account = (*AppAccount)(nil)
|
||||
|
@ -41,7 +44,9 @@ func GetAccountDecoder(cdc *wire.Codec) sdk.AccountDecoder {
|
|||
|
||||
// State to Unmarshal
|
||||
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
|
||||
|
@ -55,7 +60,7 @@ func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
|
|||
return &GenesisAccount{
|
||||
Name: aa.Name,
|
||||
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) {
|
||||
baseAcc := auth.BaseAccount{
|
||||
Address: ga.Address,
|
||||
Coins: ga.Coins,
|
||||
Coins: ga.Coins.Sort(),
|
||||
}
|
||||
return &AppAccount{
|
||||
BaseAccount: baseAcc,
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
|
||||
"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")
|
||||
}
|
||||
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
|
||||
// get the from address from the name flag
|
||||
from, err := builder.GetFromAddress()
|
||||
from, err := ctx.GetFromAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -37,7 +39,7 @@ func QuizTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
name := viper.GetString(client.FlagName)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
res, err := builder.SignBuildBroadcast(name, msg, cdc)
|
||||
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -58,8 +60,10 @@ func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
return errors.New("You must provide an answer")
|
||||
}
|
||||
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
|
||||
// get the from address from the name flag
|
||||
from, err := builder.GetFromAddress()
|
||||
from, err := ctx.GetFromAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -71,7 +75,7 @@ func SetTrendTxCmd(cdc *wire.Codec) *cobra.Command {
|
|||
msg := cool.NewSetTrendMsg(from, args[0])
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
res, err := builder.SignBuildBroadcast(name, msg, cdc)
|
||||
res, err := ctx.SignBuildBroadcast(name, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
package cool
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
// Cool genesis state, containing the genesis trend
|
||||
type GenesisState struct {
|
||||
trend string
|
||||
}
|
||||
|
||||
// Keeper - handlers sets/gets of custom variables for your module
|
||||
type Keeper struct {
|
||||
ck bank.CoinKeeper
|
||||
|
@ -49,11 +42,7 @@ func (k Keeper) CheckTrend(ctx sdk.Context, guessedTrend string) bool {
|
|||
}
|
||||
|
||||
// InitGenesis - store the genesis trend
|
||||
func (k Keeper) InitGenesis(ctx sdk.Context, data json.RawMessage) error {
|
||||
var state GenesisState
|
||||
if err := json.Unmarshal(data, &state); err != nil {
|
||||
return err
|
||||
}
|
||||
k.setTrend(ctx, state.trend)
|
||||
func (k Keeper) InitGenesis(ctx sdk.Context, data CoolGenesis) error {
|
||||
k.setTrend(ctx, data.Trend)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -15,6 +15,11 @@ type SetTrendMsg struct {
|
|||
Cool string
|
||||
}
|
||||
|
||||
// Genesis state - specify genesis trend
|
||||
type CoolGenesis struct {
|
||||
Trend string `json:"trend"`
|
||||
}
|
||||
|
||||
// New cool message
|
||||
func NewSetTrendMsg(sender sdk.Address, cool string) SetTrendMsg {
|
||||
return SetTrendMsg{
|
||||
|
|
|
@ -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)
|
||||
|
||||
// Set a handler Route.
|
||||
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore), nil)
|
||||
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore))
|
||||
|
||||
// Load latest version.
|
||||
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
|
@ -39,7 +38,7 @@ func NewApp(rootDir string, logger log.Logger) (abci.Application, error) {
|
|||
baseApp.SetInitChainer(InitChainer(capKeyMainStore))
|
||||
|
||||
// Set a handler Route.
|
||||
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore), nil)
|
||||
baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore))
|
||||
|
||||
// Load latest version.
|
||||
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
|
||||
|
@ -107,7 +106,7 @@ func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci
|
|||
// GenInitOptions can be passed into InitCmd,
|
||||
// returns a static string of a few key-values that can be parsed
|
||||
// by InitChainer
|
||||
func GenInitOptions(args []string) (json.RawMessage, string, cmn.HexBytes, error) {
|
||||
func GenInitOptions(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
|
||||
opts := []byte(`{
|
||||
"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)
|
||||
|
||||
// initialize it future-way
|
||||
opts, _, _, err := GenInitOptions(nil)
|
||||
opts, err := GenInitOptions(nil, nil, "")
|
||||
require.NoError(t, err)
|
||||
req := abci.RequestInitChain{AppStateBytes: opts}
|
||||
app.InitChain(req)
|
||||
|
|
|
@ -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"
|
||||
"io/ioutil"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
"github.com/tendermint/go-crypto/keys"
|
||||
"github.com/tendermint/go-crypto/keys/words"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
)
|
||||
|
||||
// testnetInformation contains the info necessary
|
||||
// to setup a testnet including this account and validator.
|
||||
type testnetInformation struct {
|
||||
Secret string `json:"secret"`
|
||||
Secret string `json:"secret"`
|
||||
|
||||
ChainID string `json:"chain_id"`
|
||||
Account string `json:"account"`
|
||||
Validator tmtypes.GenesisValidator `json:"validator"`
|
||||
NodeID p2p.ID `json:"node_id"`
|
||||
}
|
||||
|
||||
type initCmd struct {
|
||||
genAppState GenAppState
|
||||
context *Context
|
||||
}
|
||||
|
||||
// InitCmd will initialize all files for tendermint,
|
||||
// along with proper app_state.
|
||||
// The application can pass in a function to generate
|
||||
// proper state. And may want to use GenerateCoinKey
|
||||
// to create default account(s).
|
||||
func InitCmd(gen GenAppState, logger log.Logger) *cobra.Command {
|
||||
func InitCmd(gen GenAppState, ctx *Context) *cobra.Command {
|
||||
cmd := initCmd{
|
||||
genAppState: gen,
|
||||
logger: logger,
|
||||
context: ctx,
|
||||
}
|
||||
cobraCmd := cobra.Command{
|
||||
Use: "init",
|
||||
|
@ -41,28 +51,14 @@ func InitCmd(gen GenAppState, logger log.Logger) *cobra.Command {
|
|||
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 {
|
||||
// Store testnet information as we go
|
||||
var testnetInfo testnetInformation
|
||||
|
||||
// Run the basic tendermint initialization,
|
||||
// set up a default genesis with no app_options
|
||||
config, err := tcmd.ParseConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.initTendermintFiles(config, &testnetInfo)
|
||||
config := c.context.Config
|
||||
err := c.initTendermintFiles(config, &testnetInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -72,14 +68,22 @@ func (c initCmd) run(cmd *cobra.Command, args []string) error {
|
|||
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
|
||||
appState, secret, address, err := c.genAppState(args)
|
||||
appState, err := c.genAppState(args, addr, DEFAULT_DENOM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
testnetInfo.Secret = secret
|
||||
testnetInfo.Account = address.String()
|
||||
testnetInfo.Account = addr.String()
|
||||
|
||||
// And add them to the genesis file
|
||||
genFile := config.GenesisFile()
|
||||
|
@ -108,17 +112,17 @@ func (c initCmd) initTendermintFiles(config *cfg.Config, info *testnetInformatio
|
|||
var privValidator *tmtypes.PrivValidatorFS
|
||||
if cmn.FileExists(privValFile) {
|
||||
privValidator = tmtypes.LoadPrivValidatorFS(privValFile)
|
||||
c.logger.Info("Found private validator", "path", privValFile)
|
||||
c.context.Logger.Info("Found private validator", "path", privValFile)
|
||||
} else {
|
||||
privValidator = tmtypes.GenPrivValidatorFS(privValFile)
|
||||
privValidator.Save()
|
||||
c.logger.Info("Generated private validator", "path", privValFile)
|
||||
c.context.Logger.Info("Generated private validator", "path", privValFile)
|
||||
}
|
||||
|
||||
// genesis file
|
||||
genFile := config.GenesisFile()
|
||||
if cmn.FileExists(genFile) {
|
||||
c.logger.Info("Found genesis file", "path", genFile)
|
||||
c.context.Logger.Info("Found genesis file", "path", genFile)
|
||||
} else {
|
||||
genDoc := tmtypes.GenesisDoc{
|
||||
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
|
||||
|
@ -131,7 +135,7 @@ func (c initCmd) initTendermintFiles(config *cfg.Config, info *testnetInformatio
|
|||
if err := genDoc.SaveAs(genFile); err != nil {
|
||||
return err
|
||||
}
|
||||
c.logger.Info("Generated genesis file", "path", genFile)
|
||||
c.context.Logger.Info("Generated genesis file", "path", genFile)
|
||||
}
|
||||
|
||||
// reload the config file and find our validator info
|
||||
|
@ -144,10 +148,39 @@ func (c initCmd) initTendermintFiles(config *cfg.Config, info *testnetInformatio
|
|||
info.Validator = validator
|
||||
}
|
||||
}
|
||||
info.ChainID = loadedDoc.ChainID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
// GenAppState takes the command line args, as well
|
||||
// as an address and coin denomination.
|
||||
// It returns a default app_state to be included in
|
||||
// in the genesis file.
|
||||
// This is application-specific
|
||||
type GenAppState func(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error)
|
||||
|
||||
// DefaultGenAppState expects two args: an account address
|
||||
// and a coin denomination, and gives lots of coins to that address.
|
||||
func DefaultGenAppState(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
|
||||
opts := fmt.Sprintf(`{
|
||||
"accounts": [{
|
||||
"address": "%s",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "%s",
|
||||
"amount": 9007199254740992
|
||||
}
|
||||
]
|
||||
}]
|
||||
}`, addr.String(), coinDenom)
|
||||
return json.RawMessage(opts), nil
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
// GenesisDoc involves some tendermint-specific structures we don't
|
||||
// want to parse, so we just grab it into a raw object format,
|
||||
// so we can add one line.
|
||||
|
@ -173,3 +206,30 @@ func addGenesisState(filename string, appState json.RawMessage) error {
|
|||
|
||||
return ioutil.WriteFile(filename, out, 0600)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
// GenerateCoinKey returns the address of a public key,
|
||||
// along with the secret phrase to recover the private key.
|
||||
// You can give coins to this address and return the recovery
|
||||
// phrase to the user to access them.
|
||||
func GenerateCoinKey() (sdk.Address, string, error) {
|
||||
// construct an in-memory key store
|
||||
codec, err := words.LoadCodec("english")
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
keybase := keys.New(
|
||||
dbm.NewMemDB(),
|
||||
codec,
|
||||
)
|
||||
|
||||
// generate a private key, with recovery phrase
|
||||
info, secret, err := keybase.Create("name", "pass", keys.AlgoEd25519)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
addr := info.PubKey.Address()
|
||||
return addr, secret, nil
|
||||
}
|
||||
|
|
|
@ -8,13 +8,17 @@ import (
|
|||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/mock"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
defer setupViper(t)()
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
cmd := InitCmd(mock.GenInitOptions, logger)
|
||||
err := cmd.RunE(nil, nil)
|
||||
cfg, err := tcmd.ParseConfig()
|
||||
require.Nil(t, err)
|
||||
ctx := NewContext(cfg, logger)
|
||||
cmd := InitCmd(mock.GenInitOptions, ctx)
|
||||
err = cmd.RunE(nil, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
// appGenerator lets us lazily initialize app, using home dir
|
||||
// AppCreator lets us lazily initialize app, using home dir
|
||||
// and other flags (?) to start
|
||||
type appCreator func(string, log.Logger) (abci.Application, error)
|
||||
type AppCreator func(string, log.Logger) (abci.Application, error)
|
||||
|
||||
// StartCmd runs the service passed in, either
|
||||
// stand-alone, or in-process with tendermint
|
||||
func StartCmd(app appCreator, logger log.Logger) *cobra.Command {
|
||||
func StartCmd(app AppCreator, ctx *Context) *cobra.Command {
|
||||
start := startCmd{
|
||||
appCreator: app,
|
||||
logger: logger,
|
||||
context: ctx,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "start",
|
||||
|
@ -48,16 +48,16 @@ func StartCmd(app appCreator, logger log.Logger) *cobra.Command {
|
|||
}
|
||||
|
||||
type startCmd struct {
|
||||
appCreator appCreator
|
||||
logger log.Logger
|
||||
appCreator AppCreator
|
||||
context *Context
|
||||
}
|
||||
|
||||
func (s startCmd) run(cmd *cobra.Command, args []string) error {
|
||||
if !viper.GetBool(flagWithTendermint) {
|
||||
s.logger.Info("Starting ABCI without Tendermint")
|
||||
s.context.Logger.Info("Starting ABCI without Tendermint")
|
||||
return s.startStandAlone()
|
||||
}
|
||||
s.logger.Info("Starting ABCI with Tendermint")
|
||||
s.context.Logger.Info("Starting ABCI with Tendermint")
|
||||
return s.startInProcess()
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ func (s startCmd) startStandAlone() error {
|
|||
// Generate the app in the proper dir
|
||||
addr := viper.GetString(flagAddress)
|
||||
home := viper.GetString("home")
|
||||
app, err := s.appCreator(home, s.logger)
|
||||
app, err := s.appCreator(home, s.context.Logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ func (s startCmd) startStandAlone() error {
|
|||
if err != nil {
|
||||
return errors.Errorf("Error creating listener: %v\n", err)
|
||||
}
|
||||
svr.SetLogger(s.logger.With("module", "abci-server"))
|
||||
svr.SetLogger(s.context.Logger.With("module", "abci-server"))
|
||||
svr.Start()
|
||||
|
||||
// Wait forever
|
||||
|
@ -86,13 +86,9 @@ func (s startCmd) startStandAlone() error {
|
|||
}
|
||||
|
||||
func (s startCmd) startInProcess() error {
|
||||
cfg, err := tcmd.ParseConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := s.context.Config
|
||||
home := cfg.RootDir
|
||||
app, err := s.appCreator(home, s.logger)
|
||||
app, err := s.appCreator(home, s.context.Logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -103,7 +99,7 @@ func (s startCmd) startInProcess() error {
|
|||
proxy.NewLocalClientCreator(app),
|
||||
node.DefaultGenesisDocProviderFunc(cfg),
|
||||
node.DefaultDBProvider,
|
||||
s.logger.With("module", "node"))
|
||||
s.context.Logger.With("module", "node"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
// "os"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -9,47 +10,56 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/mock"
|
||||
"github.com/tendermint/abci/server"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
||||
func TestStartStandAlone(t *testing.T) {
|
||||
defer setupViper(t)()
|
||||
home, err := ioutil.TempDir("", "mock-sdk-cmd")
|
||||
defer func() {
|
||||
os.RemoveAll(home)
|
||||
}()
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
initCmd := InitCmd(mock.GenInitOptions, logger)
|
||||
err := initCmd.RunE(nil, nil)
|
||||
cfg, err := tcmd.ParseConfig()
|
||||
require.Nil(t, err)
|
||||
ctx := NewContext(cfg, logger)
|
||||
initCmd := InitCmd(mock.GenInitOptions, ctx)
|
||||
err = initCmd.RunE(nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// set up app and start up
|
||||
viper.Set(flagWithTendermint, false)
|
||||
viper.Set(flagAddress, "localhost:11122")
|
||||
startCmd := StartCmd(mock.NewApp, logger)
|
||||
startCmd.Flags().Set(flagAddress, FreeTCPAddr(t)) // set to a new free address
|
||||
timeout := time.Duration(3) * time.Second
|
||||
app, err := mock.NewApp(home, logger)
|
||||
require.Nil(t, err)
|
||||
svr, err := server.NewServer(FreeTCPAddr(t), "socket", app)
|
||||
require.Nil(t, err, "Error creating listener")
|
||||
svr.SetLogger(logger.With("module", "abci-server"))
|
||||
svr.Start()
|
||||
|
||||
RunOrTimeout(startCmd, timeout, t)
|
||||
timer := time.NewTimer(time.Duration(5) * time.Second)
|
||||
select {
|
||||
case <-timer.C:
|
||||
svr.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestStartWithTendermint(t *testing.T) {
|
||||
defer setupViper(t)()
|
||||
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).
|
||||
With("module", "mock-cmd")
|
||||
// logger := log.NewNopLogger()
|
||||
initCmd := InitCmd(mock.GenInitOptions, logger)
|
||||
err := initCmd.RunE(nil, nil)
|
||||
cfg, err := tcmd.ParseConfig()
|
||||
require.Nil(t, err)
|
||||
ctx := NewContext(cfg, logger)
|
||||
initCmd := InitCmd(mock.GenInitOptions, ctx)
|
||||
err = initCmd.RunE(nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// set up app and start up
|
||||
viper.Set(flagWithTendermint, true)
|
||||
startCmd := StartCmd(mock.NewApp, logger)
|
||||
startCmd := StartCmd(mock.NewApp, ctx)
|
||||
startCmd.Flags().Set(flagAddress, FreeTCPAddr(t)) // set to a new free address
|
||||
timeout := time.Duration(3) * time.Second
|
||||
timeout := time.Duration(5) * time.Second
|
||||
|
||||
//a, _ := startCmd.Flags().GetString(flagAddress)
|
||||
//panic(a)
|
||||
|
||||
RunOrTimeout(startCmd, timeout, t)
|
||||
close(RunOrTimeout(startCmd, timeout, t))
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
@ -44,14 +45,18 @@ func setupViper(t *testing.T) func() {
|
|||
func StartServer(t *testing.T) chan error {
|
||||
defer setupViper(t)()
|
||||
|
||||
cfg, err := tcmd.ParseConfig()
|
||||
require.Nil(t, err)
|
||||
|
||||
// init server
|
||||
initCmd := InitCmd(mock.GenInitOptions, log.NewNopLogger())
|
||||
err := initCmd.RunE(nil, nil)
|
||||
ctx := NewContext(cfg, log.NewNopLogger())
|
||||
initCmd := InitCmd(mock.GenInitOptions, ctx)
|
||||
err = initCmd.RunE(nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// start server
|
||||
viper.Set(flagWithTendermint, true)
|
||||
startCmd := StartCmd(mock.NewApp, log.NewNopLogger())
|
||||
startCmd := StartCmd(mock.NewApp, ctx)
|
||||
startCmd.Flags().Set(flagAddress, FreeTCPAddr(t)) // set to a new free address
|
||||
startCmd.Flags().Set("rpc.laddr", FreeTCPAddr(t)) // set to a new free address
|
||||
timeout := time.Duration(3) * time.Second
|
||||
|
|
|
@ -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) {
|
||||
if len(req.Data) == 0 {
|
||||
msg := "Query cannot be zero length"
|
||||
return sdk.ErrTxDecode(msg).Result().ToQuery()
|
||||
return sdk.ErrTxDecode(msg).QueryResult()
|
||||
}
|
||||
|
||||
tree := st.tree
|
||||
|
@ -175,7 +175,7 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
|||
|
||||
default:
|
||||
msg := fmt.Sprintf("Unexpected Query path: %v", req.Path)
|
||||
return sdk.ErrUnknownRequest(msg).Result().ToQuery()
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -205,18 +205,18 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery {
|
|||
path := req.Path
|
||||
storeName, subpath, err := parsePath(path)
|
||||
if err != nil {
|
||||
return err.Result().ToQuery()
|
||||
return err.QueryResult()
|
||||
}
|
||||
|
||||
store := rs.getStoreByName(storeName)
|
||||
if store == nil {
|
||||
msg := fmt.Sprintf("no such store: %s", storeName)
|
||||
return sdk.ErrUnknownRequest(msg).Result().ToQuery()
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
}
|
||||
queryable, ok := store.(Queryable)
|
||||
if !ok {
|
||||
msg := fmt.Sprintf("store %s doesn't support queries", storeName)
|
||||
return sdk.ErrUnknownRequest(msg).Result().ToQuery()
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
}
|
||||
|
||||
// trim the path and make the query
|
||||
|
|
|
@ -257,7 +257,10 @@ func (coins Coins) Swap(i, j int) { coins[i], coins[j] = coins[j], coins[i]
|
|||
var _ sort.Interface = Coins{}
|
||||
|
||||
// Sort is a helper function to sort the set of coins inplace
|
||||
func (coins Coins) Sort() { sort.Sort(coins) }
|
||||
func (coins Coins) Sort() Coins {
|
||||
sort.Sort(coins)
|
||||
return coins
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Parsing
|
||||
|
|
|
@ -3,6 +3,8 @@ package types
|
|||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// ABCI Response Code
|
||||
|
@ -121,6 +123,7 @@ type Error interface {
|
|||
TraceCause(cause error, msg string) Error
|
||||
Cause() error
|
||||
Result() Result
|
||||
QueryResult() abci.ResponseQuery
|
||||
}
|
||||
|
||||
func NewError(code CodeType, msg string) Error {
|
||||
|
@ -220,3 +223,11 @@ func (err *sdkError) Result() Result {
|
|||
Log: err.ABCILog(),
|
||||
}
|
||||
}
|
||||
|
||||
// QueryResult allows us to return sdk.Error.QueryResult() in query responses
|
||||
func (err *sdkError) QueryResult() abci.ResponseQuery {
|
||||
return abci.ResponseQuery{
|
||||
Code: uint32(err.ABCICode()),
|
||||
Log: err.ABCILog(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,11 +38,3 @@ type Result struct {
|
|||
func (res Result) IsOK() bool {
|
||||
return res.Code.IsOK()
|
||||
}
|
||||
|
||||
// ToQuery allows us to return sdk.Error.Result() in query responses
|
||||
func (res Result) ToQuery() abci.ResponseQuery {
|
||||
return abci.ResponseQuery{
|
||||
Code: uint32(res.Code),
|
||||
Log: res.Log,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ var (
|
|||
func getVersion() string {
|
||||
v := Version
|
||||
if GitCommit != "" {
|
||||
v = v + " " + GitCommit
|
||||
v = v + "-" + GitCommit
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ package version
|
|||
|
||||
const Maj = "0"
|
||||
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
|
||||
var GitCommit = ""
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
@ -64,7 +64,9 @@ func (c commander) getAccountCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
key := sdk.Address(bz)
|
||||
|
||||
res, err := builder.Query(key, c.storeName)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
|
||||
res, err := ctx.Query(key, c.storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
)
|
||||
|
@ -21,6 +21,7 @@ type commander struct {
|
|||
|
||||
func QueryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder sdk.AccountDecoder) func(http.ResponseWriter, *http.Request) {
|
||||
c := commander{storeName, cdc, decoder}
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
addr := vars["address"]
|
||||
|
@ -33,7 +34,7 @@ func QueryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder sdk.A
|
|||
}
|
||||
key := sdk.Address(bz)
|
||||
|
||||
res, err := builder.Query(key, c.storeName)
|
||||
res, err := ctx.Query(key, c.storeName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error())))
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
|
@ -38,8 +38,10 @@ type Commander struct {
|
|||
}
|
||||
|
||||
func (c Commander) sendTxCmd(cmd *cobra.Command, args []string) error {
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
|
||||
// get the from address
|
||||
from, err := builder.GetFromAddress()
|
||||
from, err := ctx.GetFromAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -59,14 +61,11 @@ func (c Commander) sendTxCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
to := sdk.Address(bz)
|
||||
|
||||
// get account name
|
||||
name := viper.GetString(client.FlagName)
|
||||
|
||||
// build message
|
||||
msg := BuildMsg(from, to, coins)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
res, err := builder.SignBuildBroadcast(name, msg, c.Cdc)
|
||||
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, c.Cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -195,7 +195,7 @@ func (out Output) ValidateBasic() sdk.Error {
|
|||
}
|
||||
|
||||
func (out Output) String() string {
|
||||
return fmt.Sprintf("Output{%X,%v}", out.Address, out.Coins)
|
||||
return fmt.Sprintf("Output{%v,%v}", out.Address, out.Coins)
|
||||
}
|
||||
|
||||
// NewOutput - create a transaction output, used with SendMsg
|
||||
|
|
|
@ -179,8 +179,10 @@ func TestSendMsgValidation(t *testing.T) {
|
|||
|
||||
func TestSendMsgString(t *testing.T) {
|
||||
// Construct a SendMsg
|
||||
addr1 := sdk.Address([]byte("input"))
|
||||
addr2 := sdk.Address([]byte("output"))
|
||||
addr1String := "input"
|
||||
addr2String := "output"
|
||||
addr1 := sdk.Address([]byte(addr1String))
|
||||
addr2 := sdk.Address([]byte(addr2String))
|
||||
coins := sdk.Coins{{"atom", 10}}
|
||||
var msg = SendMsg{
|
||||
Inputs: []Input{NewInput(addr1, coins)},
|
||||
|
@ -188,8 +190,9 @@ func TestSendMsgString(t *testing.T) {
|
|||
}
|
||||
|
||||
res := msg.String()
|
||||
expected := fmt.Sprintf("SendMsg{[Input{%X,10atom}]->[Output{%X,10atom}]}", addr1String, addr2String)
|
||||
// TODO some failures for bad results
|
||||
assert.Equal(t, res, "SendMsg{[Input{696E707574,10atom}]->[Output{364637353734373037353734,10atom}]}")
|
||||
assert.Equal(t, expected, res)
|
||||
}
|
||||
|
||||
func TestSendMsgGet(t *testing.T) {
|
||||
|
@ -275,16 +278,18 @@ func TestIssueMsgValidation(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIssueMsgString(t *testing.T) {
|
||||
addrString := "loan-from-bank"
|
||||
bankerString := "input"
|
||||
// Construct a IssueMsg
|
||||
addr := sdk.Address([]byte("loan-from-bank"))
|
||||
addr := sdk.Address([]byte(addrString))
|
||||
coins := sdk.Coins{{"atom", 10}}
|
||||
var msg = IssueMsg{
|
||||
Banker: sdk.Address([]byte("input")),
|
||||
Banker: sdk.Address([]byte(bankerString)),
|
||||
Outputs: []Output{NewOutput(addr, coins)},
|
||||
}
|
||||
res := msg.String()
|
||||
// TODO: FIX THIS OUTPUT!
|
||||
assert.Equal(t, res, "IssueMsg{696E707574#[Output{36433646363136453244363637323646364432443632363136453642,10atom}]}")
|
||||
expected := fmt.Sprintf("IssueMsg{%X#[Output{%X,10atom}]}", bankerString, addrString)
|
||||
assert.Equal(t, expected, res)
|
||||
}
|
||||
|
||||
func TestIssueMsgGet(t *testing.T) {
|
||||
|
|
|
@ -7,11 +7,9 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tendermint/go-crypto/keys"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/commands"
|
||||
|
@ -30,6 +28,7 @@ type sendBody struct {
|
|||
// SendRequestHandler - http request handler to send coins to a address
|
||||
func SendRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWriter, *http.Request) {
|
||||
c := commands.Commander{cdc}
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// collect data
|
||||
vars := mux.Vars(r)
|
||||
|
@ -73,9 +72,8 @@ func SendRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWrit
|
|||
}
|
||||
|
||||
// sign
|
||||
// XXX: OMG
|
||||
viper.Set(client.FlagSequence, m.Sequence)
|
||||
txBytes, err := builder.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
|
||||
ctx = ctx.WithSequence(m.Sequence)
|
||||
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -83,7 +81,7 @@ func SendRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWrit
|
|||
}
|
||||
|
||||
// send
|
||||
res, err := builder.BroadcastTx(txBytes)
|
||||
res, err := ctx.BroadcastTx(txBytes)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -1,25 +1,157 @@
|
|||
# IBC CLI Usage
|
||||
# IBC Doubble Hubble
|
||||
|
||||
## initialize
|
||||
## Remove remaining data
|
||||
|
||||
```bash
|
||||
basecoind init # copy the recover key
|
||||
basecli keys add keyname --recover
|
||||
basecoind start
|
||||
```console
|
||||
> rm -r ~/.chain1
|
||||
> rm -r ~/.chain2
|
||||
> rm -r ~/.basecli
|
||||
```
|
||||
|
||||
## transfer
|
||||
## Initialize both chains
|
||||
|
||||
`transfer` sends coins from one chain to another(or itself).
|
||||
```console
|
||||
> basecoind init --home ~/.chain1
|
||||
I[04-02|14:03:33.704] Generated private validator module=main path=/home/mossid/.chain1/config/priv_validator.json
|
||||
I[04-02|14:03:33.705] Generated genesis file module=main path=/home/mossid/.chain1/config/genesis.json
|
||||
{
|
||||
"secret": "crunch ignore trigger neither differ dance cheap brick situate floor luxury citizen husband decline arrow abandon",
|
||||
"account": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC",
|
||||
"validator": {
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "8C9917D5E982E221F5A1450103102B44BBFC1E8768126C606246CB37B5794F4D"
|
||||
},
|
||||
"power": 10,
|
||||
"name": ""
|
||||
},
|
||||
"node_id": "3ac8e6242315fd62143dc3e52c161edaaa6b1a64",
|
||||
"chain_id": "test-chain-ZajMfr"
|
||||
}
|
||||
> ADDR1=C69FEB398A29AAB1B3C4F07DE22208F35E711BCC
|
||||
> ID1=test-chain-ZajMfr
|
||||
> NODE1=tcp://0.0.0.0:36657
|
||||
> basecli keys add key1 --recover
|
||||
Enter a passphrase for your key:
|
||||
Repeat the passphrase:
|
||||
Enter your recovery seed phrase:
|
||||
crunch ignore trigger neither differ dance cheap brick situate floor luxury citizen husband decline arrow abandon
|
||||
key1 C69FEB398A29AAB1B3C4F07DE22208F35E711BCC
|
||||
|
||||
|
||||
> basecoind init --home ~/.chain2
|
||||
I[04-02|14:09:14.453] Generated private validator module=main path=/home/mossid/.chain2/config/priv_validator.json
|
||||
I[04-02|14:09:14.453] Generated genesis file module=main path=/home/mossid/.chain2/config/genesis.json
|
||||
{
|
||||
"secret": "age guide awesome month female left oxygen soccer define high grocery work desert dinner arena abandon",
|
||||
"account": "DC26002735D3AA9573707CFA6D77C12349E49868",
|
||||
"validator": {
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "A94FE4B9AD763D301F4DD5A2766009812495FB7A79F1275FB8A5AF09B44FD5F3"
|
||||
},
|
||||
"power": 10,
|
||||
"name": ""
|
||||
},
|
||||
"node_id": "ad26831330e1c72b85276d53c20f0680e6fd4cf5"
|
||||
"chain_id": "test-chain-4XHTPn"
|
||||
}
|
||||
> ADDR2=DC26002735D3AA9573707CFA6D77C12349E49868
|
||||
> ID2=test-chain-4XHTPn
|
||||
> NODE2=tcp://0.0.0.0:46657
|
||||
> basecli keys add key2 --recover
|
||||
Enter a passphrase for your key:
|
||||
Repeat the passphrase:
|
||||
Enter your recovery seed phrase:
|
||||
age guide awesome month female left oxygen soccer define high grocery work desert dinner arena abandon
|
||||
key2 DC26002735D3AA9573707CFA6D77C12349E49868
|
||||
|
||||
|
||||
> basecoind start --home ~/.chain1 --address tcp://0.0.0.0:36658 --rpc.laddr tcp://0.0.0.0:36657 --p2p.laddr tcp://0.0.0.0:36656
|
||||
...
|
||||
|
||||
> basecoind start --home ~/.chain2 # --address tcp://0.0.0.0:46658 --rpc.laddr tcp://0.0.0.0:46657 --p2p.laddr tcp://0.0.0.0:46656
|
||||
...
|
||||
```
|
||||
## Check balance
|
||||
|
||||
```console
|
||||
> basecli account $ADDR1 --node $NODE1
|
||||
{
|
||||
"address": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "mycoin",
|
||||
"amount": 9007199254740992
|
||||
}
|
||||
],
|
||||
"public_key": null,
|
||||
"sequence": 0,
|
||||
"name": ""
|
||||
}
|
||||
|
||||
> basecli account $ADDR2 --node $NODE2
|
||||
{
|
||||
"address": "DC26002735D3AA9573707CFA6D77C12349E49868",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "mycoin",
|
||||
"amount": 9007199254740992
|
||||
}
|
||||
],
|
||||
"public_key": null,
|
||||
"sequence": 0,
|
||||
"name": ""
|
||||
}
|
||||
|
||||
```bash
|
||||
basecli transfer --name keyname --to address_of_destination --amount 10mycoin --chain test-chain-AAAAAA --chain-id AAAAAA
|
||||
```
|
||||
|
||||
The id of the chain can be found in `$HOME/.basecoind/config/genesis.json`
|
||||
## Transfer coins (addr1:chain1 -> addr2:chain2)
|
||||
|
||||
```console
|
||||
> basecli transfer --name key1 --to $ADDR2 --amount 10mycoin --chain $ID2 --chain-id $ID1 --node $NODE1
|
||||
Password to sign with 'key1':
|
||||
Committed at block 1022. Hash: E16019DCC4AA08CA70AFCFBC96028ABCC51B6AD0
|
||||
> basecli account $ADDR1 --node $NODE1
|
||||
{
|
||||
"address": "C69FEB398A29AAB1B3C4F07DE22208F35E711BCC",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "mycoin",
|
||||
"amount": 9007199254740982
|
||||
}
|
||||
],
|
||||
"public_key": {
|
||||
"type": "ed25519",
|
||||
"data": "9828FF1780A066A0D93D840737566B697035448D6C880807322BED8919348B2B"
|
||||
},
|
||||
"sequence": 1,
|
||||
"name": ""
|
||||
}
|
||||
```
|
||||
|
||||
## Relay IBC packets
|
||||
|
||||
```console
|
||||
> basecli relay --name key2 --from-chain-id $ID1 --from-chain-node $NODE1 --to-chain-id $ID2 --to-chain-node $NODE2 --chain-id $ID2
|
||||
Password to sign with 'key2':
|
||||
I[04-03|16:18:59.984] Detected IBC packet number=0
|
||||
I[04-03|16:19:00.869] Relayed IBC packet number=0
|
||||
> basecli account $ADDR2 --node $NODE2
|
||||
{
|
||||
"address": "DC26002735D3AA9573707CFA6D77C12349E49868",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "mycoin",
|
||||
"amount": 9007199254741002
|
||||
}
|
||||
],
|
||||
"public_key": {
|
||||
"type": "ed25519",
|
||||
"data": "F52B4FA545F4E9BFE5D7AF1DD2236899FDEF905F9B3057C38D7C01BF1B8EB52E"
|
||||
},
|
||||
"sequence": 1,
|
||||
"name": ""
|
||||
}
|
||||
|
||||
## relay
|
||||
|
||||
```bash
|
||||
basecli relay --name keyname --from-chain-id test-chain-AAAAAA --from-chain-node=tcp://0.0.0.0:46657 --to-chain-id test-chain-AAAAAA --to-chain-node=tcp://0.0.0.0:46657
|
||||
```
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||
|
@ -39,8 +39,10 @@ type sendCommander struct {
|
|||
}
|
||||
|
||||
func (c sendCommander) sendIBCTransfer(cmd *cobra.Command, args []string) error {
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
|
||||
// get the from address
|
||||
from, err := builder.GetFromAddress()
|
||||
from, err := ctx.GetFromAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -52,9 +54,7 @@ func (c sendCommander) sendIBCTransfer(cmd *cobra.Command, args []string) error
|
|||
}
|
||||
|
||||
// get password
|
||||
name := viper.GetString(client.FlagName)
|
||||
|
||||
res, err := builder.SignBuildBroadcast(name, msg, c.cdc)
|
||||
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, c.cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ func buildMsg(from sdk.Address) (sdk.Msg, error) {
|
|||
}
|
||||
to := sdk.Address(bz)
|
||||
|
||||
packet := ibc.NewIBCPacket(from, to, coins, client.FlagChainID,
|
||||
packet := ibc.NewIBCPacket(from, to, coins, viper.GetString(client.FlagChainID),
|
||||
viper.GetString(flagChain))
|
||||
|
||||
msg := ibc.IBCTransferMsg{
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
wire "github.com/cosmos/cosmos-sdk/wire"
|
||||
|
@ -30,6 +31,8 @@ type relayCommander struct {
|
|||
decoder sdk.AccountDecoder
|
||||
mainStore string
|
||||
ibcStore string
|
||||
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
|
||||
|
@ -38,6 +41,8 @@ func IBCRelayCmd(cdc *wire.Codec) *cobra.Command {
|
|||
decoder: authcmd.GetAccountDecoder(cdc),
|
||||
ibcStore: "ibc",
|
||||
mainStore: "main",
|
||||
|
||||
logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -68,7 +73,7 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) {
|
|||
fromChainNode := viper.GetString(FlagFromChainNode)
|
||||
toChainID := viper.GetString(FlagToChainID)
|
||||
toChainNode := viper.GetString(FlagToChainNode)
|
||||
address, err := builder.GetFromAddress()
|
||||
address, err := context.NewCoreContextFromViper().GetFromAddress()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -78,35 +83,34 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
|
||||
func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) {
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
// get password
|
||||
name := viper.GetString(client.FlagName)
|
||||
passphrase, err := builder.GetPassphraseFromStdin(name)
|
||||
passphrase, err := ctx.GetPassphraseFromStdin(ctx.FromAddressName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ingressKey := ibc.IngressSequenceKey(fromChainID)
|
||||
|
||||
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:
|
||||
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)
|
||||
egressLengthbz, err := query(fromChainNode, lengthKey, c.ibcStore)
|
||||
if err != nil {
|
||||
fmt.Printf("Error querying outgoing packet list length: '%s'\n", err)
|
||||
c.logger.Error("Error querying outgoing packet list length", "err", err)
|
||||
continue OUTER
|
||||
}
|
||||
var egressLength int64
|
||||
|
@ -115,43 +119,37 @@ OUTER:
|
|||
} else if err = c.cdc.UnmarshalBinary(egressLengthbz, &egressLength); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("egressLength queried: %d\n", egressLength)
|
||||
if egressLength > processed {
|
||||
c.logger.Info("Detected IBC packet", "number", egressLength-1)
|
||||
}
|
||||
|
||||
seq := c.getSequence(toChainNode)
|
||||
|
||||
for i := processed; i < egressLength; i++ {
|
||||
egressbz, err := query(fromChainNode, ibc.EgressKey(toChainID, i), c.ibcStore)
|
||||
if err != nil {
|
||||
fmt.Printf("Error querying egress packet: '%s'\n", err)
|
||||
c.logger.Error("Error querying egress packet", "err", err)
|
||||
continue OUTER
|
||||
}
|
||||
|
||||
err = c.broadcastTx(toChainNode, c.refine(egressbz, i, passphrase))
|
||||
err = c.broadcastTx(seq, toChainNode, c.refine(egressbz, i, passphrase))
|
||||
seq++
|
||||
if err != nil {
|
||||
fmt.Printf("Error broadcasting ingress packet: '%s'\n", err)
|
||||
c.logger.Error("Error broadcasting ingress packet", "err", err)
|
||||
continue OUTER
|
||||
}
|
||||
|
||||
fmt.Printf("Relayed packet: %d\n", i)
|
||||
c.logger.Info("Relayed IBC packet", "number", i)
|
||||
}
|
||||
|
||||
processed = egressLength
|
||||
}
|
||||
}
|
||||
|
||||
func query(node string, key []byte, storeName string) (res []byte, err error) {
|
||||
orig := viper.GetString(client.FlagNode)
|
||||
viper.Set(client.FlagNode, node)
|
||||
res, err = builder.Query(key, storeName)
|
||||
viper.Set(client.FlagNode, orig)
|
||||
return res, err
|
||||
return context.NewCoreContextFromViper().WithNodeURI(node).Query(key, storeName)
|
||||
}
|
||||
|
||||
func (c relayCommander) broadcastTx(node string, tx []byte) error {
|
||||
orig := viper.GetString(client.FlagNode)
|
||||
viper.Set(client.FlagNode, node)
|
||||
seq := c.getSequence(node) + 1
|
||||
viper.Set(client.FlagSequence, seq)
|
||||
_, err := builder.BroadcastTx(tx)
|
||||
viper.Set(client.FlagNode, orig)
|
||||
func (c relayCommander) broadcastTx(seq int64, node string, tx []byte) error {
|
||||
_, err := context.NewCoreContextFromViper().WithNodeURI(node).WithSequence(seq + 1).BroadcastTx(tx)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -160,6 +158,7 @@ func (c relayCommander) getSequence(node string) int64 {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
account, err := c.decoder(res)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -180,8 +179,8 @@ func (c relayCommander) refine(bz []byte, sequence int64, passphrase string) []b
|
|||
Sequence: sequence,
|
||||
}
|
||||
|
||||
name := viper.GetString(client.FlagName)
|
||||
res, err := builder.SignAndBuild(name, passphrase, msg, c.cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.SignAndBuild(ctx.FromAddressName, passphrase, msg, c.cdc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -7,11 +7,9 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tendermint/go-crypto/keys"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/commands"
|
||||
|
@ -31,6 +29,7 @@ type transferBody struct {
|
|||
// on a different chain via IBC
|
||||
func TransferRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWriter, *http.Request) {
|
||||
c := commands.Commander{cdc}
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// collect data
|
||||
vars := mux.Vars(r)
|
||||
|
@ -71,9 +70,8 @@ func TransferRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.Response
|
|||
msg := ibc.IBCTransferMsg{packet}
|
||||
|
||||
// sign
|
||||
// XXX: OMG
|
||||
viper.Set(client.FlagSequence, m.Sequence)
|
||||
txBytes, err := builder.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
|
||||
ctx = ctx.WithSequence(m.Sequence)
|
||||
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
@ -81,7 +79,7 @@ func TransferRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.Response
|
|||
}
|
||||
|
||||
// send
|
||||
res, err := builder.BroadcastTx(txBytes)
|
||||
res, err := ctx.BroadcastTx(txBytes)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
|
|
|
@ -9,8 +9,7 @@ import (
|
|||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/simplestake"
|
||||
|
@ -48,7 +47,9 @@ type commander struct {
|
|||
}
|
||||
|
||||
func (co commander) bondTxCmd(cmd *cobra.Command, args []string) error {
|
||||
from, err := builder.GetFromAddress()
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
|
||||
from, err := ctx.GetFromAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -82,7 +83,7 @@ func (co commander) bondTxCmd(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 {
|
||||
return err
|
||||
}
|
||||
|
@ -93,8 +94,8 @@ func (co commander) unbondTxCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
func (co commander) sendMsg(msg sdk.Msg) error {
|
||||
name := viper.GetString(client.FlagName)
|
||||
res, err := builder.SignBuildBroadcast(name, msg, co.cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, co.cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire" // XXX fix
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
|
@ -47,7 +47,8 @@ func GetCmdQueryCandidates(cdc *wire.Codec, storeName string) *cobra.Command {
|
|||
|
||||
key := PrefixedKey(stake.MsgType, stake.CandidatesKey)
|
||||
|
||||
res, err := builder.Query(key, storeName)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.Query(key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -87,7 +88,9 @@ func GetCmdQueryCandidate(cdc *wire.Codec, storeName string) *cobra.Command {
|
|||
|
||||
key := PrefixedKey(stake.MsgType, stake.GetCandidateKey(addr))
|
||||
|
||||
res, err := builder.Query(key, storeName)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
|
||||
res, err := ctx.Query(key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -133,7 +136,9 @@ func GetCmdQueryDelegatorBond(cdc *wire.Codec, storeName string) *cobra.Command
|
|||
|
||||
key := PrefixedKey(stake.MsgType, stake.GetDelegatorBondKey(delegator, addr, cdc))
|
||||
|
||||
res, err := builder.Query(key, storeName)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
|
||||
res, err := ctx.Query(key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -175,7 +180,9 @@ func GetCmdQueryDelegatorBonds(cdc *wire.Codec, storeName string) *cobra.Command
|
|||
|
||||
key := PrefixedKey(stake.MsgType, stake.GetDelegatorBondsKey(delegator, cdc))
|
||||
|
||||
res, err := builder.Query(key, storeName)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
|
||||
res, err := ctx.Query(key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
|
@ -92,8 +92,8 @@ func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command {
|
|||
msg := stake.NewMsgDeclareCandidacy(candidateAddr, pk, amount, description)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
name := viper.GetString(client.FlagName)
|
||||
res, err := builder.SignBuildBroadcast(name, msg, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -129,8 +129,8 @@ func GetCmdEditCandidacy(cdc *wire.Codec) *cobra.Command {
|
|||
msg := stake.NewMsgEditCandidacy(candidateAddr, description)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
name := viper.GetString(client.FlagName)
|
||||
res, err := builder.SignBuildBroadcast(name, msg, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -165,8 +165,8 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
|
|||
msg := stake.NewMsgDelegate(delegatorAddr, candidateAddr, amount)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
name := viper.GetString(client.FlagName)
|
||||
res, err := builder.SignBuildBroadcast(name, msg, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -212,8 +212,8 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
|
|||
msg := stake.NewMsgUnbond(delegatorAddr, candidateAddr, sharesStr)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
name := viper.GetString(client.FlagName)
|
||||
res, err := builder.SignBuildBroadcast(name, msg, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.SignBuildBroadcast(ctx.FromAddressName, msg, cdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -78,6 +78,9 @@ func ErrBadDelegatorAddr() sdk.Error {
|
|||
func ErrCandidateExistsAddr() sdk.Error {
|
||||
return newError(CodeInvalidValidator, "Candidate already exist, cannot re-declare candidacy")
|
||||
}
|
||||
func ErrCandidateRevoked() sdk.Error {
|
||||
return newError(CodeInvalidValidator, "Candidacy for this address is currently revoked")
|
||||
}
|
||||
func ErrMissingSignature() sdk.Error {
|
||||
return newError(CodeInvalidValidator, "Missing signature")
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
)
|
||||
|
||||
//nolint
|
||||
|
@ -15,40 +16,6 @@ const (
|
|||
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 {
|
||||
|
@ -69,16 +36,16 @@ func NewHandler(k Keeper, ck bank.CoinKeeper) sdk.Handler {
|
|||
}
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
//_______________________________________________
|
||||
|
||||
// XXX should be send in the msg (init in CLI)
|
||||
//func getSender() sdk.Address {
|
||||
//signers := msg.GetSigners()
|
||||
//if len(signers) != 1 {
|
||||
//return sdk.ErrUnauthorized("there can only be one signer for staking transaction").Result()
|
||||
//}
|
||||
//sender := signers[0]
|
||||
//}
|
||||
// NewEndBlocker generates sdk.EndBlocker
|
||||
// Performs tick functionality
|
||||
func NewEndBlocker(k Keeper) sdk.EndBlocker {
|
||||
return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
|
||||
res.ValidatorUpdates = k.Tick(ctx)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
|
@ -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
|
||||
// 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 {
|
||||
|
@ -145,25 +116,29 @@ func handleMsgDelegate(ctx sdk.Context, msg MsgDelegate, k Keeper) sdk.Result {
|
|||
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
|
||||
return ErrBadBondingDenom().Result()
|
||||
}
|
||||
if candidate.Status == Revoked {
|
||||
return ErrCandidateRevoked().Result()
|
||||
}
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{
|
||||
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 {
|
||||
|
||||
if candidate.Status == Revoked { //candidate has been withdrawn
|
||||
return ErrBondNotNominated()
|
||||
}
|
||||
|
||||
// Get or create the delegator bond
|
||||
existingBond, found := k.getDelegatorBond(ctx, delegatorAddr, candidate.Address)
|
||||
bond, found := k.getDelegatorBond(ctx, delegatorAddr, candidate.Address)
|
||||
if !found {
|
||||
existingBond = DelegatorBond{
|
||||
bond = DelegatorBond{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
CandidateAddr: candidate.Address,
|
||||
Shares: sdk.ZeroRat,
|
||||
|
@ -171,25 +146,17 @@ func delegateWithCandidate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
|
|||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
k.setDelegatorBond(ctx, existingBond)
|
||||
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)
|
||||
pool, candidate, newShares := pool.candidateAddTokens(candidate, bondAmt.Amount)
|
||||
bond.Shares = bond.Shares.Add(newShares)
|
||||
|
||||
k.setDelegatorBond(ctx, bond)
|
||||
k.setCandidate(ctx, candidate)
|
||||
k.setPool(ctx, pool)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -216,7 +183,7 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
|||
return ErrNotEnoughBondShares(msg.Shares).Result()
|
||||
}
|
||||
} else {
|
||||
if !bond.Shares.GT(shares) {
|
||||
if bond.Shares.LT(shares) {
|
||||
return ErrNotEnoughBondShares(msg.Shares).Result()
|
||||
}
|
||||
}
|
||||
|
@ -258,16 +225,19 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
|||
}
|
||||
|
||||
// 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}}
|
||||
k.coinKeeper.AddCoins(ctx, bond.DelegatorAddr, returnCoins)
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
// revoke candidate if necessary
|
||||
if revokeCandidacy {
|
||||
|
||||
// change the share types to unbonded if they were not already
|
||||
if candidate.Status == Bonded {
|
||||
k.bondedToUnbondedPool(ctx, candidate)
|
||||
p, candidate = p.bondedToUnbondedPool(candidate)
|
||||
}
|
||||
|
||||
// lastly update the status
|
||||
|
@ -280,25 +250,43 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
|||
} else {
|
||||
k.setCandidate(ctx, candidate)
|
||||
}
|
||||
k.setPool(ctx, p)
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
// XXX where this used
|
||||
// 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 {
|
||||
// TODO use or remove
|
||||
//// 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) (DelegatorBond, Candidate, Pool, sdk.Error) {
|
||||
|
||||
// subtract bond tokens from delegator bond
|
||||
if bond.Shares.LT(shares) {
|
||||
return sdk.ErrInsufficientFunds("") //XXX variables inside
|
||||
}
|
||||
bond.Shares = bond.Shares.Sub(shares)
|
||||
//pool := k.GetPool(ctx)
|
||||
//_, err := k.coinKeeper.SubtractCoins(ctx, bond.DelegatorAddr, sdk.Coins{amount})
|
||||
//if err != nil {
|
||||
//return bond, candidate, pool, err
|
||||
//}
|
||||
//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)
|
||||
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
|
||||
//pool := k.GetPool(ctx)
|
||||
|
||||
_, err := k.coinKeeper.AddCoins(ctx, candidate.Address, returnCoins)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
//// subtract bond tokens from delegator bond
|
||||
//if bond.Shares.LT(shares) {
|
||||
//errMsg := fmt.Sprintf("cannot unbond %v shares, only have %v shares available", shares, bond.Shares)
|
||||
//return bond, candidate, pool, sdk.ErrInsufficientFunds(errMsg)
|
||||
//}
|
||||
//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
|
||||
|
||||
//import (
|
||||
//"strconv"
|
||||
//"testing"
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
//"github.com/stretchr/testify/assert"
|
||||
//"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"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 {
|
||||
//return MsgDeclareCandidacy{
|
||||
//Description: Description{},
|
||||
//CandidateAddr: address,
|
||||
//Bond: sdk.Coin{"fermion", amt},
|
||||
//PubKey: pubKey,
|
||||
//}
|
||||
//}
|
||||
func newTestMsgDeclareCandidacy(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgDeclareCandidacy {
|
||||
return MsgDeclareCandidacy{
|
||||
Description: Description{},
|
||||
CandidateAddr: address,
|
||||
Bond: sdk.Coin{"fermion", amt},
|
||||
PubKey: pubKey,
|
||||
}
|
||||
}
|
||||
|
||||
//func newTestMsgDelegate(amt int64, delegatorAddr, candidateAddr sdk.Address) MsgDelegate {
|
||||
//return MsgDelegate{
|
||||
//DelegatorAddr: delegatorAddr,
|
||||
//CandidateAddr: candidateAddr,
|
||||
//Bond: sdk.Coin{"fermion", amt},
|
||||
//}
|
||||
//}
|
||||
func newTestMsgDelegate(delegatorAddr, candidateAddr sdk.Address, amt int64) MsgDelegate {
|
||||
return MsgDelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
CandidateAddr: candidateAddr,
|
||||
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)
|
||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
//assert.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
||||
func TestDuplicatesMsgDeclareCandidacy(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 1000)
|
||||
|
||||
//// one sender can bond to two different addresses
|
||||
//msgDeclareCandidacy.Address = addrs[1]
|
||||
//err := checker.declareCandidacy(msgDeclareCandidacy)
|
||||
//assert.Nil(t, err, "didn't expected error on checkTx")
|
||||
candidateAddr := addrs[0]
|
||||
pk := pks[0]
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pk, 10)
|
||||
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
|
||||
//checker.sender = addrs[1]
|
||||
//msgDeclareCandidacy.Address = addrs[0]
|
||||
//err = checker.declareCandidacy(msgDeclareCandidacy)
|
||||
//assert.NotNil(t, err, "expected error on checkTx")
|
||||
//}
|
||||
// one candidate cannot bond twice
|
||||
msgDeclareCandidacy.PubKey = pks[1]
|
||||
got = handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
assert.False(t, got.IsOK(), "%v", got)
|
||||
}
|
||||
|
||||
//func TestIncrementsMsgDelegate(t *testing.T) {
|
||||
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 1000)
|
||||
func TestIncrementsMsgDelegate(t *testing.T) {
|
||||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||
params := keeper.GetParams(ctx)
|
||||
|
||||
//// first declare candidacy
|
||||
//bondAmount := int64(10)
|
||||
//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,
|
||||
bondAmount := int64(10)
|
||||
candidateAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
|
||||
//// just send the same msgbond multiple times
|
||||
//msgDelegate := newTestMsgDelegate(bondAmount, addrs[0])
|
||||
//for i := 0; i < 5; i++ {
|
||||
//got := deliverer.delegate(msgDelegate)
|
||||
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
// first declare candidacy
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[0], bondAmount)
|
||||
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
assert.True(t, got.IsOK(), "expected declare candidacy msg to be ok, got %v", got)
|
||||
|
||||
////Check that the accounts and the bond account have the appropriate values
|
||||
//candidates := mapper.GetCandidates()
|
||||
//expectedBond += bondAmount
|
||||
////expectedSender := initSender - expectedBond
|
||||
//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
|
||||
//}
|
||||
//}
|
||||
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||
require.True(t, found)
|
||||
assert.Equal(t, bondAmount, candidate.Liabilities.Evaluate())
|
||||
assert.Equal(t, bondAmount, candidate.Assets.Evaluate())
|
||||
|
||||
//func TestIncrementsMsgUnbond(t *testing.T) {
|
||||
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 0)
|
||||
// just send the same msgbond multiple times
|
||||
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
|
||||
//initBond := int64(1000)
|
||||
////accStore[string(deliverer.sender)] = initBond //XXX use StoreMapper
|
||||
//got := deliverer.declareCandidacy(newTestMsgDeclareCandidacy(addrs[0], pks[0], initBond))
|
||||
//assert.NoError(t, got, "expected initial bond msg to be ok, got %v", got)
|
||||
//Check that the accounts and the bond account have the appropriate values
|
||||
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||
require.True(t, found)
|
||||
bond, found := keeper.getDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||
require.True(t, found)
|
||||
|
||||
//// just send the same msgunbond multiple times
|
||||
//// XXX use decimals here
|
||||
//unbondShares, unbondSharesStr := int64(10), "10"
|
||||
//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)
|
||||
expBond := int64(i+1) * bondAmount
|
||||
expLiabilities := int64(i+2) * bondAmount // (1 self delegation)
|
||||
expDelegatorAcc := initBond - expBond
|
||||
|
||||
////Check that the accounts and the bond account have the appropriate values
|
||||
//candidates := mapper.GetCandidates()
|
||||
//expectedBond := initBond - int64(i+1)*unbondShares // +1 since we send 1 at the start of loop
|
||||
////expectedSender := initSender + (initBond - expectedBond)
|
||||
//gotBonded := candidates[0].Liabilities.Evaluate()
|
||||
////gotSender := accStore[string(deliverer.sender)] // XXX use storemapper
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
gotLiabilities := candidate.Liabilities.Evaluate()
|
||||
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
|
||||
|
||||
//assert.Equal(t, expectedBond, gotBonded, "%v, %v", expectedBond, gotBonded)
|
||||
////assert.Equal(t, expectedSender, gotSender, "%v, %v", expectedSender, gotSender) //XXX fix
|
||||
//}
|
||||
require.Equal(t, expBond, gotBond,
|
||||
"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
|
||||
//errorCases := []int64{
|
||||
////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))
|
||||
//msgUndelegate := NewMsgUnbond(addrs[0], unbondShares)
|
||||
//got = deliverer.unbond(msgUndelegate)
|
||||
//assert.Error(t, got, "expected unbond msg to fail")
|
||||
//}
|
||||
func TestIncrementsMsgUnbond(t *testing.T) {
|
||||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||
params := keeper.GetParams(ctx)
|
||||
|
||||
//leftBonded := initBond - unbondShares*int64(nUnbonds)
|
||||
// declare candidacy, delegate
|
||||
candidateAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
|
||||
//// should be unable to unbond one more than we have
|
||||
//msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)+1))
|
||||
//got = deliverer.unbond(msgUndelegate)
|
||||
//assert.Error(t, got, "expected unbond msg to fail")
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[0], initBond)
|
||||
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
assert.True(t, got.IsOK(), "expected declare-candidacy to be ok, got %v", got)
|
||||
|
||||
//// should be able to unbond just what we have
|
||||
//msgUndelegate = NewMsgUnbond(addrs[0], strconv.Itoa(int(leftBonded)))
|
||||
//got = deliverer.unbond(msgUndelegate)
|
||||
//assert.NoError(t, got, "expected unbond msg to pass")
|
||||
//}
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, candidateAddr, initBond)
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
assert.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
|
||||
|
||||
//func TestMultipleMsgDeclareCandidacy(t *testing.T) {
|
||||
//initSender := int64(1000)
|
||||
//ctx, accStore, mapper, deliverer := createTestInput(t, addrs[0], false, initSender)
|
||||
//addrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
|
||||
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||
require.True(t, found)
|
||||
assert.Equal(t, initBond*2, candidate.Liabilities.Evaluate())
|
||||
assert.Equal(t, initBond*2, candidate.Assets.Evaluate())
|
||||
|
||||
//// bond them all
|
||||
//for i, addr := range addrs {
|
||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[i], pks[i], 10)
|
||||
//deliverer.sender = addr
|
||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
//assert.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
// just send the same msgUnbond multiple times
|
||||
// TODO use decimals here
|
||||
unbondShares, unbondSharesStr := int64(10), "10"
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, candidateAddr, unbondSharesStr)
|
||||
numUnbonds := 5
|
||||
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
|
||||
//candidates := mapper.GetCandidates()
|
||||
//require.Equal(t, i, len(candidates))
|
||||
//val := candidates[i]
|
||||
//balanceExpd := initSender - 10
|
||||
//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)
|
||||
//}
|
||||
//Check that the accounts and the bond account have the appropriate values
|
||||
candidate, found = keeper.GetCandidate(ctx, candidateAddr)
|
||||
require.True(t, found)
|
||||
bond, found := keeper.getDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||
require.True(t, found)
|
||||
|
||||
//// unbond them all
|
||||
//for i, addr := range addrs {
|
||||
//candidatePre := mapper.GetCandidate(addrs[i])
|
||||
//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)
|
||||
expBond := initBond - int64(i+1)*unbondShares
|
||||
expLiabilities := 2*initBond - int64(i+1)*unbondShares
|
||||
expDelegatorAcc := initBond - expBond
|
||||
|
||||
////Check that the account is unbonded
|
||||
//candidates := mapper.GetCandidates()
|
||||
//assert.Equal(t, len(addrs)-(i+1), len(candidates), "expected %d candidates got %d", len(addrs)-(i+1), len(candidates))
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
gotLiabilities := candidate.Liabilities.Evaluate()
|
||||
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
|
||||
|
||||
//candidatePost := mapper.GetCandidate(addrs[i])
|
||||
//balanceExpd := initSender
|
||||
//balanceGot := accStore.GetAccount(ctx, candidatePre.Address).GetCoins()
|
||||
//assert.Nil(t, candidatePost, "expected nil candidate retrieve, got %d", 0, candidatePost)
|
||||
//assert.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
|
||||
//}
|
||||
//}
|
||||
require.Equal(t, expBond, gotBond,
|
||||
"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)
|
||||
}
|
||||
|
||||
//func TestMultipleMsgDelegate(t *testing.T) {
|
||||
//sender, delegators := addrs[0], addrs[1:]
|
||||
//_, _, mapper, deliverer := createTestInput(t, addrs[0], false, 1000)
|
||||
// these are more than we have bonded now
|
||||
errorCases := []int64{
|
||||
//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
|
||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(sender, pks[0], 10)
|
||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
//require.NoError(t, got, "expected msg to be ok, got %v", got)
|
||||
leftBonded := initBond - unbondShares*int64(numUnbonds)
|
||||
|
||||
//// delegate multiple parties
|
||||
//for i, delegator := range delegators {
|
||||
//msgDelegate := newTestMsgDelegate(10, sender)
|
||||
//deliverer.sender = delegator
|
||||
//got := deliverer.delegate(msgDelegate)
|
||||
//require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
// should be unable to unbond one more than we have
|
||||
unbondSharesStr = strconv.Itoa(int(leftBonded) + 1)
|
||||
msgUnbond = NewMsgUnbond(delegatorAddr, candidateAddr, unbondSharesStr)
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
assert.False(t, got.IsOK(),
|
||||
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
|
||||
|
||||
////Check that the account is bonded
|
||||
//bond := mapper.getDelegatorBond(delegator, sender)
|
||||
//assert.NotNil(t, bond, "expected delegatee bond %d to exist", bond)
|
||||
//}
|
||||
// should be able to unbond just what we have
|
||||
unbondSharesStr = strconv.Itoa(int(leftBonded))
|
||||
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
|
||||
//for i, delegator := range delegators {
|
||||
//msgUndelegate := NewMsgUnbond(sender, "10")
|
||||
//deliverer.sender = delegator
|
||||
//got := deliverer.unbond(msgUndelegate)
|
||||
//require.NoError(t, got, "expected msg %d to be ok, got %v", i, got)
|
||||
func TestMultipleMsgDeclareCandidacy(t *testing.T) {
|
||||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||
params := keeper.GetParams(ctx)
|
||||
candidateAddrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
|
||||
|
||||
////Check that the account is unbonded
|
||||
//bond := mapper.getDelegatorBond(delegator, sender)
|
||||
//assert.Nil(t, bond, "expected delegatee bond %d to be nil", bond)
|
||||
//}
|
||||
//}
|
||||
// bond them all
|
||||
for i, candidateAddr := range candidateAddrs {
|
||||
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) {
|
||||
//sender, delegator := addrs[0], addrs[1]
|
||||
//_, _, _, deliverer := createTestInput(t, addrs[0], false, 1000)
|
||||
//Check that the account is bonded
|
||||
candidates := keeper.GetCandidates(ctx, 100)
|
||||
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
|
||||
//msgDeclareCandidacy := newTestMsgDeclareCandidacy(addrs[0], pks[0], 10)
|
||||
//got := deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
||||
// unbond them all
|
||||
for i, candidateAddr := range candidateAddrs {
|
||||
candidatePre, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||
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
|
||||
//msgDelegate := newTestMsgDelegate(10, addrs[0])
|
||||
//deliverer.sender = delegator
|
||||
//got = deliverer.delegate(msgDelegate)
|
||||
//require.NoError(t, got, "expected ok, got %v", got)
|
||||
//Check that the account is unbonded
|
||||
candidates := keeper.GetCandidates(ctx, 100)
|
||||
require.Equal(t, len(candidateAddrs)-(i+1), len(candidates),
|
||||
"expected %d candidates got %d", len(candidateAddrs)-(i+1), len(candidates))
|
||||
|
||||
//// unbond the candidates bond portion
|
||||
//msgUndelegate := NewMsgUnbond(addrs[0], "10")
|
||||
//deliverer.sender = sender
|
||||
//got = deliverer.unbond(msgUndelegate)
|
||||
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
||||
_, found = keeper.GetCandidate(ctx, candidateAddr)
|
||||
require.False(t, found)
|
||||
|
||||
//// test that this pubkey cannot yet be bonded too
|
||||
//deliverer.sender = delegator
|
||||
//got = deliverer.delegate(msgDelegate)
|
||||
//assert.Error(t, got, "expected error, got %v", got)
|
||||
expBalance := initBond
|
||||
gotBalance := accMapper.GetAccount(ctx, candidatePre.Address).GetCoins().AmountOf(params.BondDenom)
|
||||
require.Equal(t, expBalance, gotBalance, "expected account to have %d, got %d", expBalance, gotBalance)
|
||||
}
|
||||
}
|
||||
|
||||
//// test that the delegator can still withdraw their bonds
|
||||
//got = deliverer.unbond(msgUndelegate)
|
||||
//require.NoError(t, got, "expected no error on runMsgDeclareCandidacy")
|
||||
func TestMultipleMsgDelegate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 1000)
|
||||
candidateAddr, delegatorAddrs := addrs[0], addrs[1:]
|
||||
|
||||
//// verify that the pubkey can now be reused
|
||||
//got = deliverer.declareCandidacy(msgDeclareCandidacy)
|
||||
//assert.NoError(t, got, "expected ok, got %v", got)
|
||||
//}
|
||||
//first make a candidate
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[0], 10)
|
||||
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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -81,30 +96,47 @@ func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) {
|
|||
store.Set(GetCandidateKey(candidate.Address), bz)
|
||||
|
||||
// mashal the new validator record
|
||||
validator := Validator{address, candidate.Assets}
|
||||
validator := candidate.validator()
|
||||
bz, err = k.cdc.MarshalBinary(validator)
|
||||
if err != nil {
|
||||
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
|
||||
if oldFound {
|
||||
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
|
||||
if store.Get(GetRecentValidatorKey(address)) == nil {
|
||||
return
|
||||
}
|
||||
store.Set(GetAccUpdateValidatorKey(validator.Address), bz)
|
||||
// or is a new validator
|
||||
setAcc := false
|
||||
if store.Get(GetRecentValidatorKey(address)) != nil {
|
||||
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) {
|
||||
|
||||
// first retreive the old candidate record
|
||||
oldCandidate, found := k.GetCandidate(ctx, address)
|
||||
candidate, found := k.GetCandidate(ctx, address)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
@ -112,39 +144,104 @@ func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) {
|
|||
// delete the old candidate record
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store.Delete(GetCandidateKey(address))
|
||||
store.Delete(GetValidatorKey(address, candidate.Assets, k.cdc))
|
||||
|
||||
// delete from recent and power weighted validator groups if the validator
|
||||
// exists and add validator with zero power to the validator updates
|
||||
if store.Get(GetRecentValidatorKey(address)) == nil {
|
||||
return
|
||||
}
|
||||
bz, err := k.cdc.MarshalBinary(Validator{address, sdk.ZeroRat})
|
||||
bz, err := k.cdc.MarshalBinary(candidate.validator().abciValidatorZero(k.cdc))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
store.Set(GetAccUpdateValidatorKey(address), bz)
|
||||
store.Delete(GetRecentValidatorKey(address))
|
||||
store.Delete(GetValidatorKey(address, oldCandidate.Assets, k.cdc))
|
||||
}
|
||||
|
||||
//___________________________________________________________________________
|
||||
|
||||
// get the most recent updated validator set from the Candidates. These bonds
|
||||
// are already sorted by Assets from the UpdateVotingPower function which
|
||||
// is the only function which is to modify the Assets
|
||||
// this function also updaates the most recent validators saved in store
|
||||
// Get the validator set from the candidates. The correct subset is retrieved
|
||||
// by iterating through an index of the candidates sorted by power, stored
|
||||
// using the ValidatorsKey. Simultaniously the most recent the validator
|
||||
// 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) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
// clear the recent validators store
|
||||
k.deleteSubSpace(store, RecentValidatorsKey)
|
||||
// clear the recent validators store, add to the ToKickOut Temp store
|
||||
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
|
||||
maxVal := k.GetParams(ctx).MaxValidators
|
||||
iterator := store.ReverseIterator(subspace(ValidatorsKey)) //smallest to largest
|
||||
validators = make([]Validator, maxVal)
|
||||
maxValidators := k.GetParams(ctx).MaxValidators
|
||||
iterator = store.ReverseIterator(subspace(ValidatorsKey)) // largest to smallest
|
||||
validators = make([]Validator, maxValidators)
|
||||
i := 0
|
||||
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) {
|
||||
iterator.Close()
|
||||
break
|
||||
|
@ -155,15 +252,13 @@ func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
validators[i] = val
|
||||
|
||||
// also add to the recent validators group
|
||||
store.Set(GetRecentValidatorKey(val.Address), bz)
|
||||
|
||||
if bytes.Equal(val.Address, address) {
|
||||
return true
|
||||
}
|
||||
iterator.Next()
|
||||
}
|
||||
|
||||
return validators[:i] // trim
|
||||
return false
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// 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)
|
||||
|
||||
iterator := store.Iterator(subspace(AccUpdateValidatorsKey)) //smallest to largest
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
valBytes := iterator.Value()
|
||||
var val Validator
|
||||
var val abci.Validator
|
||||
err := k.cdc.UnmarshalBinary(valBytes, &val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -199,12 +294,9 @@ func (k Keeper) getAccUpdateValidators(ctx sdk.Context) (updates []Validator) {
|
|||
// remove all validator update entries
|
||||
func (k Keeper) clearAccUpdateValidators(ctx sdk.Context) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
k.deleteSubSpace(store, AccUpdateValidatorsKey)
|
||||
}
|
||||
|
||||
// TODO move to common functionality somewhere
|
||||
func (k Keeper) deleteSubSpace(store sdk.KVStore, key []byte) {
|
||||
iterator := store.Iterator(subspace(key))
|
||||
// delete subspace
|
||||
iterator := store.Iterator(subspace(AccUpdateValidatorsKey))
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
store.Delete(iterator.Key())
|
||||
}
|
||||
|
@ -279,8 +371,7 @@ func (k Keeper) GetParams(ctx sdk.Context) (params Params) {
|
|||
store := ctx.KVStore(k.storeKey)
|
||||
b := store.Get(ParamKey)
|
||||
if b == nil {
|
||||
k.params = defaultParams()
|
||||
return k.params
|
||||
panic("Stored params should not have been nil")
|
||||
}
|
||||
|
||||
err := k.cdc.UnmarshalBinary(b, ¶ms)
|
||||
|
@ -298,3 +389,33 @@ func (k Keeper) setParams(ctx sdk.Context, params Params) {
|
|||
store.Set(ParamKey, b)
|
||||
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
|
||||
ValidatorsKey = []byte{0x03} // prefix for each key to a validator
|
||||
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
|
||||
|
@ -43,6 +45,16 @@ func GetRecentValidatorKey(addr sdk.Address) []byte {
|
|||
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
|
||||
func GetDelegatorBondKey(delegatorAddr, candidateAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetDelegatorBondsKey(delegatorAddr, cdc), candidateAddr.Bytes()...)
|
||||
|
|
|
@ -2,6 +2,7 @@ package stake
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -12,38 +13,34 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
addrDel1 = addrs[0]
|
||||
addrDel2 = addrs[1]
|
||||
addrVal1 = addrs[2]
|
||||
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),
|
||||
addrDels = []sdk.Address{
|
||||
addrs[0],
|
||||
addrs[1],
|
||||
}
|
||||
candidate2 = Candidate{
|
||||
Address: addrVal2,
|
||||
PubKey: pk2,
|
||||
Assets: sdk.NewRat(9),
|
||||
Liabilities: sdk.NewRat(9),
|
||||
}
|
||||
candidate3 = Candidate{
|
||||
Address: addrVal3,
|
||||
PubKey: pk3,
|
||||
Assets: sdk.NewRat(9),
|
||||
Liabilities: sdk.NewRat(9),
|
||||
addrVals = []sdk.Address{
|
||||
addrs[2],
|
||||
addrs[3],
|
||||
addrs[4],
|
||||
addrs[5],
|
||||
addrs[6],
|
||||
}
|
||||
)
|
||||
|
||||
// This function tests GetCandidate, GetCandidates, setCandidate, removeCandidate
|
||||
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 {
|
||||
return c1.Status == c2.Status &&
|
||||
|
@ -55,60 +52,72 @@ func TestCandidate(t *testing.T) {
|
|||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.GetCandidate(ctx, addrVal1)
|
||||
_, found := keeper.GetCandidate(ctx, addrVals[0])
|
||||
assert.False(t, found)
|
||||
resCands := keeper.GetCandidates(ctx, 100)
|
||||
assert.Zero(t, len(resCands))
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.setCandidate(ctx, candidate1)
|
||||
resCand, found := keeper.GetCandidate(ctx, addrVal1)
|
||||
keeper.setCandidate(ctx, candidates[0])
|
||||
resCand, found := keeper.GetCandidate(ctx, addrVals[0])
|
||||
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
|
||||
candidate1.Liabilities = sdk.NewRat(99)
|
||||
keeper.setCandidate(ctx, candidate1)
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVal1)
|
||||
candidates[0].Liabilities = sdk.NewRat(99)
|
||||
keeper.setCandidate(ctx, candidates[0])
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVals[0])
|
||||
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
|
||||
resCands = keeper.GetCandidates(ctx, 100)
|
||||
require.Equal(t, 1, len(resCands))
|
||||
assert.Equal(t, addrVal1, resCands[0].Address)
|
||||
assert.Equal(t, addrVals[0], resCands[0].Address)
|
||||
|
||||
// add other candidates
|
||||
keeper.setCandidate(ctx, candidate2)
|
||||
keeper.setCandidate(ctx, candidate3)
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVal2)
|
||||
keeper.setCandidate(ctx, candidates[1])
|
||||
keeper.setCandidate(ctx, candidates[2])
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVals[1])
|
||||
require.True(t, found)
|
||||
assert.True(t, candidatesEqual(candidate2, resCand), "%v \n %v", resCand, candidate2)
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVal3)
|
||||
assert.True(t, candidatesEqual(candidates[1], resCand), "%v \n %v", resCand, candidates[1])
|
||||
resCand, found = keeper.GetCandidate(ctx, addrVals[2])
|
||||
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)
|
||||
require.Equal(t, 3, len(resCands))
|
||||
assert.True(t, candidatesEqual(candidate1, resCands[0]), "%v \n %v", resCands[0], candidate1)
|
||||
assert.True(t, candidatesEqual(candidate2, resCands[1]), "%v \n %v", resCands[1], candidate2)
|
||||
assert.True(t, candidatesEqual(candidate3, resCands[2]), "%v \n %v", resCands[2], candidate3)
|
||||
assert.True(t, candidatesEqual(candidates[0], resCands[0]), "%v \n %v", resCands[0], candidates[0])
|
||||
assert.True(t, candidatesEqual(candidates[1], resCands[1]), "%v \n %v", resCands[1], candidates[1])
|
||||
assert.True(t, candidatesEqual(candidates[2], resCands[2]), "%v \n %v", resCands[2], candidates[2])
|
||||
|
||||
// remove a record
|
||||
keeper.removeCandidate(ctx, candidate2.Address)
|
||||
_, found = keeper.GetCandidate(ctx, addrVal2)
|
||||
keeper.removeCandidate(ctx, candidates[1].Address)
|
||||
_, found = keeper.GetCandidate(ctx, addrVals[1])
|
||||
assert.False(t, found)
|
||||
}
|
||||
|
||||
// tests GetDelegatorBond, GetDelegatorBonds, SetDelegatorBond, removeDelegatorBond
|
||||
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
|
||||
keeper.setCandidate(ctx, candidate1)
|
||||
//construct the candidates
|
||||
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{
|
||||
DelegatorAddr: addrDel1,
|
||||
CandidateAddr: addrVal1,
|
||||
DelegatorAddr: addrDels[0],
|
||||
CandidateAddr: addrVals[0],
|
||||
Shares: sdk.NewRat(9),
|
||||
}
|
||||
|
||||
|
@ -119,30 +128,30 @@ func TestBond(t *testing.T) {
|
|||
}
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.getDelegatorBond(ctx, addrDel1, addrVal1)
|
||||
_, found := keeper.getDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||
assert.False(t, found)
|
||||
|
||||
// set and retrieve a record
|
||||
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, bondsEqual(bond1to1, resBond))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
bond1to1.Shares = sdk.NewRat(99)
|
||||
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, bondsEqual(bond1to1, resBond))
|
||||
|
||||
// add some more records
|
||||
keeper.setCandidate(ctx, candidate2)
|
||||
keeper.setCandidate(ctx, candidate3)
|
||||
bond1to2 := DelegatorBond{addrDel1, addrVal2, sdk.NewRat(9)}
|
||||
bond1to3 := DelegatorBond{addrDel1, addrVal3, sdk.NewRat(9)}
|
||||
bond2to1 := DelegatorBond{addrDel2, addrVal1, sdk.NewRat(9)}
|
||||
bond2to2 := DelegatorBond{addrDel2, addrVal2, sdk.NewRat(9)}
|
||||
bond2to3 := DelegatorBond{addrDel2, addrVal3, sdk.NewRat(9)}
|
||||
keeper.setCandidate(ctx, candidates[1])
|
||||
keeper.setCandidate(ctx, candidates[2])
|
||||
bond1to2 := DelegatorBond{addrDels[0], addrVals[1], sdk.NewRat(9)}
|
||||
bond1to3 := DelegatorBond{addrDels[0], addrVals[2], sdk.NewRat(9)}
|
||||
bond2to1 := DelegatorBond{addrDels[1], addrVals[0], sdk.NewRat(9)}
|
||||
bond2to2 := DelegatorBond{addrDels[1], addrVals[1], sdk.NewRat(9)}
|
||||
bond2to3 := DelegatorBond{addrDels[1], addrVals[2], sdk.NewRat(9)}
|
||||
keeper.setDelegatorBond(ctx, bond1to2)
|
||||
keeper.setDelegatorBond(ctx, bond1to3)
|
||||
keeper.setDelegatorBond(ctx, bond2to1)
|
||||
|
@ -150,16 +159,16 @@ func TestBond(t *testing.T) {
|
|||
keeper.setDelegatorBond(ctx, bond2to3)
|
||||
|
||||
// test all bond retrieve capabilities
|
||||
resBonds := keeper.getDelegatorBonds(ctx, addrDel1, 5)
|
||||
resBonds := keeper.getDelegatorBonds(ctx, addrDels[0], 5)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
assert.True(t, bondsEqual(bond1to1, resBonds[0]))
|
||||
assert.True(t, bondsEqual(bond1to2, resBonds[1]))
|
||||
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))
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel1, 2)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDels[0], 2)
|
||||
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))
|
||||
assert.True(t, bondsEqual(bond2to1, resBonds[0]))
|
||||
assert.True(t, bondsEqual(bond2to2, resBonds[1]))
|
||||
|
@ -167,9 +176,9 @@ func TestBond(t *testing.T) {
|
|||
|
||||
// delete a record
|
||||
keeper.removeDelegatorBond(ctx, bond2to3)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDel2, addrVal3)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[2])
|
||||
assert.False(t, found)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel2, 5)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
assert.True(t, bondsEqual(bond2to1, resBonds[0]))
|
||||
assert.True(t, bondsEqual(bond2to2, resBonds[1]))
|
||||
|
@ -177,30 +186,30 @@ func TestBond(t *testing.T) {
|
|||
// delete all the records from delegator 2
|
||||
keeper.removeDelegatorBond(ctx, bond2to1)
|
||||
keeper.removeDelegatorBond(ctx, bond2to2)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDel2, addrVal1)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[0])
|
||||
assert.False(t, found)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDel2, addrVal2)
|
||||
_, found = keeper.getDelegatorBond(ctx, addrDels[1], addrVals[1])
|
||||
assert.False(t, found)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDel2, 5)
|
||||
resBonds = keeper.getDelegatorBonds(ctx, addrDels[1], 5)
|
||||
require.Equal(t, 0, len(resBonds))
|
||||
}
|
||||
|
||||
// TODO integrate in testing for equal validators, whichever one was a validator
|
||||
// first remains the validator https://github.com/cosmos/cosmos-sdk/issues/582
|
||||
func TestGetValidators(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
// initialize some candidates into the state
|
||||
amts := []int64{0, 100, 1, 400, 200}
|
||||
n := len(amts)
|
||||
candidates := make([]Candidate, n)
|
||||
for i := 0; i < n; i++ {
|
||||
var candidates [5]Candidate
|
||||
for i, amt := range amts {
|
||||
c := Candidate{
|
||||
Status: Unbonded,
|
||||
PubKey: pks[i],
|
||||
Address: addrs[i],
|
||||
Assets: sdk.NewRat(amts[i]),
|
||||
Liabilities: sdk.NewRat(amts[i]),
|
||||
Assets: sdk.NewRat(amt),
|
||||
Liabilities: sdk.NewRat(amt),
|
||||
}
|
||||
keeper.setCandidate(ctx, c)
|
||||
candidates[i] = c
|
||||
|
@ -209,11 +218,11 @@ func TestGetValidators(t *testing.T) {
|
|||
// first make sure everything as normal is ordered
|
||||
validators := keeper.GetValidators(ctx)
|
||||
require.Equal(t, len(validators), n)
|
||||
assert.Equal(t, sdk.NewRat(400), validators[0].VotingPower, "%v", validators)
|
||||
assert.Equal(t, sdk.NewRat(200), validators[1].VotingPower, "%v", validators)
|
||||
assert.Equal(t, sdk.NewRat(100), validators[2].VotingPower, "%v", validators)
|
||||
assert.Equal(t, sdk.NewRat(1), validators[3].VotingPower, "%v", validators)
|
||||
assert.Equal(t, sdk.NewRat(0), validators[4].VotingPower, "%v", validators)
|
||||
assert.Equal(t, sdk.NewRat(400), validators[0].Power, "%v", validators)
|
||||
assert.Equal(t, sdk.NewRat(200), validators[1].Power, "%v", validators)
|
||||
assert.Equal(t, sdk.NewRat(100), validators[2].Power, "%v", validators)
|
||||
assert.Equal(t, sdk.NewRat(1), validators[3].Power, "%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[4].Address, validators[1].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])
|
||||
validators = keeper.GetValidators(ctx)
|
||||
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)
|
||||
|
||||
// test a decrease in voting power
|
||||
|
@ -233,7 +242,7 @@ func TestGetValidators(t *testing.T) {
|
|||
keeper.setCandidate(ctx, candidates[3])
|
||||
validators = keeper.GetValidators(ctx)
|
||||
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)
|
||||
|
||||
// test a swap in voting power
|
||||
|
@ -241,9 +250,9 @@ func TestGetValidators(t *testing.T) {
|
|||
keeper.setCandidate(ctx, candidates[0])
|
||||
validators = keeper.GetValidators(ctx)
|
||||
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, 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)
|
||||
|
||||
// test the max validators term
|
||||
|
@ -253,46 +262,301 @@ func TestGetValidators(t *testing.T) {
|
|||
keeper.setParams(ctx, params)
|
||||
validators = keeper.GetValidators(ctx)
|
||||
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, 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
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
|
||||
validators := keeper.GetValidators(ctx)
|
||||
assert.Equal(t, 0, len(validators))
|
||||
|
||||
// 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
|
||||
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
|
||||
assert.False(t, keeper.IsRecentValidator(ctx, candidatesIn[2].Address))
|
||||
|
||||
// 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
|
||||
assert.False(t, keeper.IsRecentValidator(ctx, candidatesIn[0].Address))
|
||||
}
|
||||
|
||||
func TestParams(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
expParams := defaultParams()
|
||||
|
||||
//check that the empty keeper loads the default
|
||||
|
@ -305,3 +569,46 @@ func TestParams(t *testing.T) {
|
|||
resParams = keeper.GetParams(ctx)
|
||||
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"
|
||||
)
|
||||
|
||||
// load/save the global staking state
|
||||
func (k Keeper) GetPool(ctx sdk.Context) (gs Pool) {
|
||||
// check if cached before anything
|
||||
if k.gs != (Pool{}) {
|
||||
return k.gs
|
||||
// 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)
|
||||
}
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
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
|
||||
return sdk.ZeroRat
|
||||
}
|
||||
|
||||
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)
|
||||
// get the exchange rate of bonded token per issued share
|
||||
func (p Pool) bondedShareExRate() sdk.Rat {
|
||||
if p.BondedShares.IsZero() {
|
||||
return sdk.OneRat
|
||||
}
|
||||
store.Set(PoolKey, b)
|
||||
k.gs = Pool{} // clear the cache
|
||||
return sdk.NewRat(p.BondedPool).Quo(p.BondedShares)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
//TODO make these next two functions more efficient should be reading and writting to state ye know
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
tokens := k.removeSharesBonded(ctx, candidate.Assets)
|
||||
candidate.Assets = k.addTokensUnbonded(ctx, tokens)
|
||||
p, tokens := p.removeSharesBonded(candidate.Assets)
|
||||
p, candidate.Assets = p.addTokensUnbonded(tokens)
|
||||
candidate.Status = Unbonded
|
||||
k.setCandidate(ctx, candidate)
|
||||
return p, candidate
|
||||
}
|
||||
|
||||
// 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
|
||||
tokens := k.removeSharesUnbonded(ctx, candidate.Assets)
|
||||
candidate.Assets = k.addTokensBonded(ctx, tokens)
|
||||
p, tokens := p.removeSharesUnbonded(candidate.Assets)
|
||||
p, candidate.Assets = p.addTokensBonded(tokens)
|
||||
candidate.Status = Bonded
|
||||
k.setCandidate(ctx, candidate)
|
||||
return p, candidate
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
func (k Keeper) addTokensBonded(ctx sdk.Context, amount int64) (issuedShares sdk.Rat) {
|
||||
p := k.GetPool(ctx)
|
||||
issuedShares = p.bondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
|
||||
func (p Pool) addTokensBonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||
issuedShares = sdk.NewRat(amount).Quo(p.bondedShareExRate()) // (tokens/shares)^-1 * tokens
|
||||
p.BondedPool += amount
|
||||
p.BondedShares = p.BondedShares.Add(issuedShares)
|
||||
k.setPool(ctx, p)
|
||||
return
|
||||
return p, issuedShares
|
||||
}
|
||||
|
||||
func (k Keeper) removeSharesBonded(ctx sdk.Context, shares sdk.Rat) (removedTokens int64) {
|
||||
p := k.GetPool(ctx)
|
||||
func (p Pool) removeSharesBonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||
removedTokens = p.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||
p.BondedShares = p.BondedShares.Sub(shares)
|
||||
p.BondedPool -= removedTokens
|
||||
k.setPool(ctx, p)
|
||||
return
|
||||
p.BondedPool = p.BondedPool - removedTokens
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
func (k Keeper) addTokensUnbonded(ctx sdk.Context, amount int64) (issuedShares sdk.Rat) {
|
||||
p := k.GetPool(ctx)
|
||||
func (p Pool) addTokensUnbonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||
issuedShares = p.unbondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
|
||||
p.UnbondedShares = p.UnbondedShares.Add(issuedShares)
|
||||
p.UnbondedPool += amount
|
||||
k.setPool(ctx, p)
|
||||
return
|
||||
return p, issuedShares
|
||||
}
|
||||
|
||||
func (k Keeper) removeSharesUnbonded(ctx sdk.Context, shares sdk.Rat) (removedTokens int64) {
|
||||
p := k.GetPool(ctx)
|
||||
func (p Pool) removeSharesUnbonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||
removedTokens = p.unbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||
p.UnbondedShares = p.UnbondedShares.Sub(shares)
|
||||
p.UnbondedPool -= removedTokens
|
||||
k.setPool(ctx, p)
|
||||
return
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// 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()
|
||||
|
||||
var receivedGlobalShares sdk.Rat
|
||||
if candidate.Status == Bonded {
|
||||
receivedGlobalShares = k.addTokensBonded(ctx, amount)
|
||||
p, receivedGlobalShares = p.addTokensBonded(amount)
|
||||
} else {
|
||||
receivedGlobalShares = k.addTokensUnbonded(ctx, amount)
|
||||
p, receivedGlobalShares = p.addTokensUnbonded(amount)
|
||||
}
|
||||
candidate.Assets = candidate.Assets.Add(receivedGlobalShares)
|
||||
|
||||
issuedDelegatorShares = exRate.Mul(receivedGlobalShares)
|
||||
candidate.Liabilities = candidate.Liabilities.Add(issuedDelegatorShares)
|
||||
k.setPool(ctx, p) // TODO cache Pool?
|
||||
return
|
||||
|
||||
return p, candidate, issuedDelegatorShares
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
globalPoolSharesToRemove := candidate.delegatorShareExRate().Mul(shares)
|
||||
if candidate.Status == Bonded {
|
||||
createdCoins = k.removeSharesBonded(ctx, globalPoolSharesToRemove)
|
||||
p, createdCoins = p.removeSharesBonded(globalPoolSharesToRemove)
|
||||
} else {
|
||||
createdCoins = k.removeSharesUnbonded(ctx, globalPoolSharesToRemove)
|
||||
p, createdCoins = p.removeSharesUnbonded(globalPoolSharesToRemove)
|
||||
}
|
||||
candidate.Assets = candidate.Assets.Sub(globalPoolSharesToRemove)
|
||||
candidate.Liabilities = candidate.Liabilities.Sub(shares)
|
||||
k.setPool(ctx, p) // TODO cache Pool?
|
||||
return
|
||||
return p, candidate, createdCoins
|
||||
}
|
||||
|
|
|
@ -1,22 +1,533 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestPool(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
expPool := initialPool()
|
||||
func TestBondedRatio(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.TotalSupply = 3
|
||||
pool.BondedPool = 2
|
||||
|
||||
//check that the empty keeper loads the default
|
||||
resPool := keeper.GetPool(ctx)
|
||||
assert.Equal(t, expPool, resPool)
|
||||
// bonded pool / total supply
|
||||
require.Equal(t, pool.bondedRatio(), sdk.NewRat(2).Quo(sdk.NewRat(3)))
|
||||
pool.TotalSupply = 0
|
||||
|
||||
//modify a params, save, and retrieve
|
||||
expPool.TotalSupply = 777
|
||||
keeper.setPool(ctx, expPool)
|
||||
resPool = keeper.GetPool(ctx)
|
||||
assert.Equal(t, expPool, resPool)
|
||||
// avoids divide-by-zero
|
||||
require.Equal(t, pool.bondedRatio(), sdk.ZeroRat)
|
||||
}
|
||||
|
||||
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"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
|
@ -52,6 +51,31 @@ var (
|
|||
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
|
||||
func subspace(prefix []byte) (start, end []byte) {
|
||||
end = make([]byte, len(prefix))
|
||||
|
@ -83,7 +107,7 @@ func makeTestCodec() *wire.Codec {
|
|||
const accTypeApp = 0x1
|
||||
var _ = oldwire.RegisterInterface(
|
||||
struct{ sdk.Account }{},
|
||||
oldwire.ConcreteType{&types.AppAccount{}, accTypeApp},
|
||||
oldwire.ConcreteType{&auth.BaseAccount{}, accTypeApp},
|
||||
)
|
||||
cdc := wire.NewCodec()
|
||||
|
||||
|
@ -105,7 +129,7 @@ func paramsNoInflation() Params {
|
|||
}
|
||||
|
||||
// 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()
|
||||
keyStake := sdk.NewKVStoreKey("stake")
|
||||
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)
|
||||
keeper := NewKeeper(ctx, cdc, keyStake, ck)
|
||||
|
||||
//params := paramsNoInflation()
|
||||
params := keeper.GetParams(ctx)
|
||||
keeper.setPool(ctx, initialPool())
|
||||
keeper.setParams(ctx, defaultParams())
|
||||
|
||||
// fill all the addresses with some coins
|
||||
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
|
||||
|
|
|
@ -6,38 +6,36 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
hrsPerYear = 8766 // as defined by a julian year of 365.25 days
|
||||
precision = 1000000000
|
||||
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
|
||||
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
|
||||
func (k Keeper) Tick(ctx sdk.Context) (change []*abci.Validator, err error) {
|
||||
|
||||
// retrieve params
|
||||
func (k Keeper) Tick(ctx sdk.Context) (change []abci.Validator) {
|
||||
p := k.GetPool(ctx)
|
||||
height := ctx.BlockHeight()
|
||||
|
||||
// Process Validator Provisions
|
||||
// XXX right now just process every 5 blocks, in new SDK make hourly
|
||||
if p.InflationLastTime+5 <= height {
|
||||
p.InflationLastTime = height
|
||||
k.processProvisions(ctx)
|
||||
blockTime := ctx.BlockHeader().Time // XXX assuming in seconds, confirm
|
||||
if p.InflationLastTime+blockTime >= 3600 {
|
||||
p.InflationLastTime = blockTime
|
||||
p = k.processProvisions(ctx)
|
||||
}
|
||||
|
||||
newVals := k.GetValidators(ctx)
|
||||
// save the params
|
||||
k.setPool(ctx, p)
|
||||
|
||||
// XXX determine change from old validators, set to change
|
||||
_ = newVals
|
||||
return change, nil
|
||||
change = k.getAccUpdateValidators(ctx)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 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.Inflation = k.nextInflation(ctx).Round(precision)
|
||||
pool.Inflation = k.nextInflation(ctx)
|
||||
|
||||
// Because the validators hold a relative bonded share (`GlobalStakeShare`), when
|
||||
// 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()
|
||||
pool.BondedPool += provisions
|
||||
pool.TotalSupply += provisions
|
||||
|
||||
// save the params
|
||||
k.setPool(ctx, pool)
|
||||
return pool
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return
|
||||
return inflation.Round(precision)
|
||||
}
|
||||
|
|
|
@ -1,116 +1,134 @@
|
|||
package stake
|
||||
|
||||
//import (
|
||||
//"testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
//sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
//"github.com/stretchr/testify/assert"
|
||||
//)
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
//func TestGetInflation(t *testing.T) {
|
||||
//ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
//params := defaultParams()
|
||||
//keeper.setParams(ctx, params)
|
||||
//gs := keeper.GetPool(ctx)
|
||||
func TestGetInflation(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
params := keeper.GetParams(ctx)
|
||||
hrsPerYrRat := sdk.NewRat(hrsPerYr)
|
||||
|
||||
//// Governing Mechanism:
|
||||
//// bondedRatio = BondedPool / TotalSupply
|
||||
//// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
|
||||
// Governing Mechanism:
|
||||
// bondedRatio = BondedPool / TotalSupply
|
||||
// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
|
||||
|
||||
//tests := []struct {
|
||||
//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)},
|
||||
tests := []struct {
|
||||
name string
|
||||
setBondedPool, setTotalSupply int64
|
||||
setInflation, expectedChange sdk.Rat
|
||||
}{
|
||||
// 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
|
||||
//{1, 1, sdk.NewRat(20, 100), sdk.OneRat.Sub(sdk.OneRat.Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYr)},
|
||||
// 100% bonded, starting at 20% inflation and being reduced
|
||||
// (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
|
||||
//{1, 2, sdk.NewRat(10, 100), sdk.OneRat.Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYr)},
|
||||
// 50% bonded, starting at 10% inflation and being increased
|
||||
{"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)
|
||||
//{1, 1, sdk.NewRat(7, 100), sdk.ZeroRat},
|
||||
//{1, 1, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000)},
|
||||
// test 7% minimum stop (testing with 100% bonded)
|
||||
{"test 4", 1, 1, sdk.NewRat(7, 100), sdk.ZeroRat},
|
||||
{"test 5", 1, 1, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
|
||||
|
||||
//// test 20% maximum stop (testing with 0% bonded)
|
||||
//{0, 0, sdk.NewRat(20, 100), sdk.ZeroRat},
|
||||
//{0, 0, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000)},
|
||||
// test 20% maximum stop (testing with 0% bonded)
|
||||
{"test 6", 0, 0, sdk.NewRat(20, 100), sdk.ZeroRat},
|
||||
{"test 7", 0, 0, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000).Round(precision)},
|
||||
|
||||
//// perfect balance shouldn't change inflation
|
||||
//{67, 100, sdk.NewRat(15, 100), sdk.ZeroRat},
|
||||
//}
|
||||
//for _, tc := range tests {
|
||||
//gs.BondedPool, p.TotalSupply = tc.setBondedPool, tc.setTotalSupply
|
||||
//gs.Inflation = tc.setInflation
|
||||
// perfect balance shouldn't change inflation
|
||||
{"test 8", 67, 100, sdk.NewRat(15, 100), sdk.ZeroRat},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
pool.BondedPool, pool.TotalSupply = tc.setBondedPool, tc.setTotalSupply
|
||||
pool.Inflation = tc.setInflation
|
||||
keeper.setPool(ctx, pool)
|
||||
|
||||
//inflation := nextInflation(gs, params)
|
||||
//diffInflation := inflation.Sub(tc.setInflation)
|
||||
inflation := keeper.nextInflation(ctx)
|
||||
diffInflation := inflation.Sub(tc.setInflation)
|
||||
|
||||
//assert.True(t, diffInflation.Equal(tc.expectedChange),
|
||||
//"%v, %v", diffInflation, tc.expectedChange)
|
||||
//}
|
||||
//}
|
||||
assert.True(t, diffInflation.Equal(tc.expectedChange),
|
||||
"Name: %v\nDiff: %v\nExpected: %v\n", tc.name, diffInflation, tc.expectedChange)
|
||||
}
|
||||
}
|
||||
|
||||
//func TestProcessProvisions(t *testing.T) {
|
||||
//ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||
//params := defaultParams()
|
||||
//keeper.setParams(ctx, params)
|
||||
//gs := keeper.GetPool(ctx)
|
||||
func TestProcessProvisions(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
params := defaultParams()
|
||||
keeper.setParams(ctx, params)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
//// create some candidates some bonded, some unbonded
|
||||
//candidates := candidatesFromAddrsEmpty(addrs)
|
||||
//for i, candidate := range candidates {
|
||||
//if i < 5 {
|
||||
//candidate.Status = Bonded
|
||||
//}
|
||||
//mintedTokens := int64((i + 1) * 10000000)
|
||||
//gs.TotalSupply += mintedTokens
|
||||
//keeper.candidateAddTokens(ctx, candidate, mintedTokens)
|
||||
//keeper.setCandidate(ctx, candidate)
|
||||
//}
|
||||
//var totalSupply int64 = 550000000
|
||||
//var bondedShares int64 = 150000000
|
||||
//var unbondedShares int64 = 400000000
|
||||
// create some candidates some bonded, some unbonded
|
||||
candidates := make([]Candidate, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
c := Candidate{
|
||||
Status: Unbonded,
|
||||
PubKey: pks[i],
|
||||
Address: addrs[i],
|
||||
Assets: sdk.NewRat(0),
|
||||
Liabilities: sdk.NewRat(0),
|
||||
}
|
||||
if i < 5 {
|
||||
c.Status = Bonded
|
||||
}
|
||||
mintedTokens := int64((i + 1) * 10000000)
|
||||
pool.TotalSupply += mintedTokens
|
||||
pool, c, _ = pool.candidateAddTokens(c, mintedTokens)
|
||||
|
||||
//// initial bonded ratio ~ 27%
|
||||
//assert.True(t, p.bondedRatio().Equal(sdk.NewRat(bondedShares, totalSupply)), "%v", p.bondedRatio())
|
||||
keeper.setCandidate(ctx, c)
|
||||
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
|
||||
//assert.Equal(t, totalSupply, p.TotalSupply)
|
||||
//assert.Equal(t, bondedShares, p.BondedPool)
|
||||
//assert.Equal(t, unbondedShares, p.UnbondedPool)
|
||||
// initial bonded ratio ~ 27%
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(bondedShares, totalSupply)), "%v", pool.bondedRatio())
|
||||
|
||||
//// test the value of candidate shares
|
||||
//assert.True(t, p.bondedShareExRate().Equal(sdk.OneRat), "%v", p.bondedShareExRate())
|
||||
// test the value of candidate shares
|
||||
assert.True(t, pool.bondedShareExRate().Equal(sdk.OneRat), "%v", pool.bondedShareExRate())
|
||||
|
||||
//initialSupply := p.TotalSupply
|
||||
//initialUnbonded := p.TotalSupply - p.BondedPool
|
||||
initialSupply := pool.TotalSupply
|
||||
initialUnbonded := pool.TotalSupply - pool.BondedPool
|
||||
|
||||
//// process the provisions a year
|
||||
//for hr := 0; hr < 8766; hr++ {
|
||||
//expInflation := nextInflation(gs, params).Round(1000000000)
|
||||
//expProvisions := (expInflation.Mul(sdk.NewRat(gs.TotalSupply)).Quo(hrsPerYr)).Evaluate()
|
||||
//startBondedPool := p.BondedPool
|
||||
//startTotalSupply := p.TotalSupply
|
||||
//processProvisions(ctx, keeper, p, params)
|
||||
//assert.Equal(t, startBondedPool+expProvisions, p.BondedPool)
|
||||
//assert.Equal(t, startTotalSupply+expProvisions, p.TotalSupply)
|
||||
//}
|
||||
//assert.NotEqual(t, initialSupply, p.TotalSupply)
|
||||
//assert.Equal(t, initialUnbonded, p.UnbondedPool)
|
||||
////panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", p.TotalSupply, p.BondedPool, p.TotalSupply-gs.BondedPool))
|
||||
// process the provisions a year
|
||||
for hr := 0; hr < 8766; hr++ {
|
||||
pool := keeper.GetPool(ctx)
|
||||
expInflation := keeper.nextInflation(ctx).Round(1000000000)
|
||||
expProvisions := (expInflation.Mul(sdk.NewRat(pool.TotalSupply)).Quo(hrsPerYrRat)).Evaluate()
|
||||
startBondedPool := pool.BondedPool
|
||||
startTotalSupply := pool.TotalSupply
|
||||
pool = keeper.processProvisions(ctx)
|
||||
keeper.setPool(ctx, pool)
|
||||
//fmt.Printf("hr %v, startBondedPool %v, expProvisions %v, pool.BondedPool %v\n", hr, startBondedPool, expProvisions, pool.BondedPool)
|
||||
require.Equal(t, startBondedPool+expProvisions, pool.BondedPool, "hr %v", hr)
|
||||
require.Equal(t, startTotalSupply+expProvisions, pool.TotalSupply)
|
||||
}
|
||||
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
|
||||
//assert.True(t, p.bondedRatio().Equal(sdk.NewRat(105906511, 305906511)), "%v", p.bondedRatio())
|
||||
// initial bonded ratio ~ from 27% to 40% increase for bonded holders ownership of total supply
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(271734723, 671734723)), "%v", pool.bondedRatio())
|
||||
|
||||
//// global supply
|
||||
//assert.Equal(t, int64(611813022), p.TotalSupply)
|
||||
//assert.Equal(t, int64(211813022), p.BondedPool)
|
||||
//assert.Equal(t, unbondedShares, p.UnbondedPool)
|
||||
// global supply
|
||||
assert.Equal(t, int64(671734723), pool.TotalSupply)
|
||||
assert.Equal(t, int64(271734723), pool.BondedPool)
|
||||
assert.Equal(t, unbondedShares, pool.UnbondedPool)
|
||||
|
||||
//// test the value of candidate shares
|
||||
//assert.True(t, p.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(211813022)), "%v", p.bondedShareExRate())
|
||||
// test the value of candidate shares
|
||||
assert.True(t, pool.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(271734723)), "%v", pool.bondedShareExRate())
|
||||
|
||||
//}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package stake
|
|||
|
||||
import (
|
||||
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"
|
||||
)
|
||||
|
||||
|
@ -16,19 +18,6 @@ type Params struct {
|
|||
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
|
||||
|
@ -42,42 +31,10 @@ type Pool struct {
|
|||
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
|
||||
}
|
||||
|
||||
// XXX define globalstate interface?
|
||||
|
||||
func initialPool() Pool {
|
||||
return Pool{
|
||||
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)
|
||||
// GenesisState - all staking state that must be provided at genesis
|
||||
type GenesisState struct {
|
||||
Pool Pool `json:"pool"`
|
||||
Params Params `json:"params"`
|
||||
}
|
||||
|
||||
//_______________________________________________________________________________________________________
|
||||
|
@ -149,8 +106,9 @@ func (c Candidate) delegatorShareExRate() sdk.Rat {
|
|||
// Should only be called when the Candidate qualifies as a validator.
|
||||
func (c Candidate) validator() Validator {
|
||||
return Validator{
|
||||
Address: c.Address, // XXX !!!
|
||||
VotingPower: c.Assets,
|
||||
Address: c.Address,
|
||||
PubKey: c.PubKey,
|
||||
Power: c.Assets,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,23 +119,35 @@ func (c Candidate) validator() Validator {
|
|||
|
||||
// Validator is one of the top Candidates
|
||||
type Validator struct {
|
||||
Address sdk.Address `json:"address"` // Address of validator
|
||||
VotingPower sdk.Rat `json:"voting_power"` // Voting power if considered a validator
|
||||
Address sdk.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
Power sdk.Rat `json:"voting_power"`
|
||||
}
|
||||
|
||||
// ABCIValidator - Get the validator from a bond value
|
||||
/* TODO
|
||||
func (v Validator) ABCIValidator() (*abci.Validator, error) {
|
||||
pkBytes, err := wire.MarshalBinary(v.PubKey)
|
||||
// abci validator from stake validator type
|
||||
func (v Validator) abciValidator(cdc *wire.Codec) abci.Validator {
|
||||
pkBytes, err := cdc.MarshalBinary(v.PubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
panic(err)
|
||||
}
|
||||
return &abci.Validator{
|
||||
return abci.Validator{
|
||||
PubKey: pkBytes,
|
||||
Power: v.VotingPower.Evaluate(),
|
||||
}, nil
|
||||
Power: v.Power.Evaluate(),
|
||||
}
|
||||
}
|
||||
|
||||
// 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