Merge branch 'develop' into release/v0.30.0
This commit is contained in:
commit
a5b08c4c4d
|
@ -64,7 +64,6 @@ jobs:
|
||||||
command: |
|
command: |
|
||||||
export PATH="$GOBIN:$PATH"
|
export PATH="$GOBIN:$PATH"
|
||||||
make install
|
make install
|
||||||
make install_examples
|
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: /tmp/workspace
|
root: /tmp/workspace
|
||||||
paths:
|
paths:
|
||||||
|
@ -103,7 +102,6 @@ jobs:
|
||||||
command: |
|
command: |
|
||||||
export PATH="$GOBIN:$PATH"
|
export PATH="$GOBIN:$PATH"
|
||||||
make test_cli
|
make test_cli
|
||||||
make test_examples
|
|
||||||
|
|
||||||
test_sim_gaia_nondeterminism:
|
test_sim_gaia_nondeterminism:
|
||||||
<<: *linux_defaults
|
<<: *linux_defaults
|
||||||
|
|
|
@ -20,7 +20,6 @@ dist
|
||||||
devtools-stamp
|
devtools-stamp
|
||||||
|
|
||||||
# Data - ideally these don't exist
|
# Data - ideally these don't exist
|
||||||
examples/basecoin/app/data
|
|
||||||
baseapp/data/*
|
baseapp/data/*
|
||||||
client/lcd/keys/*
|
client/lcd/keys/*
|
||||||
client/lcd/statik/statik.go
|
client/lcd/statik/statik.go
|
||||||
|
|
|
@ -30,6 +30,7 @@ BREAKING CHANGES
|
||||||
in order to trigger a simulation of the tx before the actual execution.
|
in order to trigger a simulation of the tx before the actual execution.
|
||||||
* [\#3285](https://github.com/cosmos/cosmos-sdk/pull/3285) New `gaiad tendermint version` to print libs versions
|
* [\#3285](https://github.com/cosmos/cosmos-sdk/pull/3285) New `gaiad tendermint version` to print libs versions
|
||||||
* [\#1894](https://github.com/cosmos/cosmos-sdk/pull/1894) `version` command now shows latest commit, vendor dir hash, and build machine info.
|
* [\#1894](https://github.com/cosmos/cosmos-sdk/pull/1894) `version` command now shows latest commit, vendor dir hash, and build machine info.
|
||||||
|
* [\#3249\(https://github.com/cosmos/cosmos-sdk/issues/3249) `tendermint`'s `show-validator` and `show-address` `--json` flags removed in favor of `--output-format=json`.
|
||||||
|
|
||||||
* SDK
|
* SDK
|
||||||
* [#3336](https://github.com/cosmos/cosmos-sdk/issues/3336) Ensure all SDK
|
* [#3336](https://github.com/cosmos/cosmos-sdk/issues/3336) Ensure all SDK
|
||||||
|
@ -91,7 +92,7 @@ FEATURES
|
||||||
the ante handler.
|
the ante handler.
|
||||||
* [\#3179](https://github.com/cosmos/cosmos-sdk/pull/3179) New CodeNoSignatures error code.
|
* [\#3179](https://github.com/cosmos/cosmos-sdk/pull/3179) New CodeNoSignatures error code.
|
||||||
* [\#3319](https://github.com/cosmos/cosmos-sdk/issues/3319) [x/distribution] Queriers for all distribution state worth querying; distribution query commands
|
* [\#3319](https://github.com/cosmos/cosmos-sdk/issues/3319) [x/distribution] Queriers for all distribution state worth querying; distribution query commands
|
||||||
|
* [\#3356](https://github.com/cosmos/cosmos-sdk/issues/3356) [x/auth] bech32-ify accounts address in error message.
|
||||||
|
|
||||||
IMPROVEMENTS
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
@ -112,8 +113,10 @@ IMPROVEMENTS
|
||||||
* Validators specify minimum gas prices instead of minimum fees
|
* Validators specify minimum gas prices instead of minimum fees
|
||||||
* Clients may provide either fees or gas prices directly
|
* Clients may provide either fees or gas prices directly
|
||||||
* The gas prices of a tx must meet a validator's minimum
|
* The gas prices of a tx must meet a validator's minimum
|
||||||
|
* `gaiad start` and `gaia.toml` take --minimum-gas-prices flag and minimum-gas-price config key respectively.
|
||||||
* [\#2859](https://github.com/cosmos/cosmos-sdk/issues/2859) Rename `TallyResult` in gov proposals to `FinalTallyResult`
|
* [\#2859](https://github.com/cosmos/cosmos-sdk/issues/2859) Rename `TallyResult` in gov proposals to `FinalTallyResult`
|
||||||
* [\#3286](https://github.com/cosmos/cosmos-sdk/pull/3286) Fix `gaiad gentx` printout of account's addresses, i.e. user bech32 instead of hex.
|
* [\#3286](https://github.com/cosmos/cosmos-sdk/pull/3286) Fix `gaiad gentx` printout of account's addresses, i.e. user bech32 instead of hex.
|
||||||
|
* [\#3249\(https://github.com/cosmos/cosmos-sdk/issues/3249) `--json` flag removed, users should use `--output=json` instead.
|
||||||
|
|
||||||
* SDK
|
* SDK
|
||||||
* [\#3137](https://github.com/cosmos/cosmos-sdk/pull/3137) Add tag documentation
|
* [\#3137](https://github.com/cosmos/cosmos-sdk/pull/3137) Add tag documentation
|
||||||
|
|
|
@ -69,7 +69,7 @@ If you open a PR on the Cosmos SDK, it is mandatory to update the relevant docum
|
||||||
|
|
||||||
* If your change relates to the core SDK (baseapp, store, ...), please update the docs/gaia folder, the docs/examples folder and possibly the docs/spec folder.
|
* If your change relates to the core SDK (baseapp, store, ...), please update the docs/gaia folder, the docs/examples folder and possibly the docs/spec folder.
|
||||||
* If your changes relate specifically to the gaia application (not including modules), please modify the docs/gaia folder.
|
* If your changes relate specifically to the gaia application (not including modules), please modify the docs/gaia folder.
|
||||||
* If your changes relate to a module, please update the module's spec in docs/spec. If the module is used by gaia and/or basecoin, you might also need to modify docs/gaia and/or docs/examples.
|
* If your changes relate to a module, please update the module's spec in docs/spec. If the module is used by gaia, you might also need to modify docs/gaia and/or docs/examples.
|
||||||
* If your changes relate to the core of the CLI or Light-client (not specifically to module's CLI/Rest), please modify the docs/clients folder.
|
* If your changes relate to the core of the CLI or Light-client (not specifically to module's CLI/Rest), please modify the docs/clients folder.
|
||||||
|
|
||||||
## Forking
|
## Forking
|
||||||
|
@ -85,7 +85,7 @@ For instance, to create a fork and work on a branch of it, I would:
|
||||||
- Create the fork on github, using the fork button.
|
- Create the fork on github, using the fork button.
|
||||||
- Go to the original repo checked out locally (i.e. `$GOPATH/src/github.com/cosmos/cosmos-sdk`)
|
- Go to the original repo checked out locally (i.e. `$GOPATH/src/github.com/cosmos/cosmos-sdk`)
|
||||||
- `git remote rename origin upstream`
|
- `git remote rename origin upstream`
|
||||||
- `git remote add origin git@github.com:ebuchman/basecoin.git`
|
- `git remote add origin git@github.com:ebuchman/cosmos-sdk.git`
|
||||||
|
|
||||||
Now `origin` refers to my fork and `upstream` refers to the Cosmos-SDK version.
|
Now `origin` refers to my fork and `upstream` refers to the Cosmos-SDK version.
|
||||||
So I can `git push -u origin master` to update my fork, and make pull requests to Cosmos-SDK from there.
|
So I can `git push -u origin master` to update my fork, and make pull requests to Cosmos-SDK from there.
|
||||||
|
|
|
@ -697,7 +697,6 @@
|
||||||
"github.com/tendermint/tendermint/crypto/secp256k1",
|
"github.com/tendermint/tendermint/crypto/secp256k1",
|
||||||
"github.com/tendermint/tendermint/crypto/tmhash",
|
"github.com/tendermint/tendermint/crypto/tmhash",
|
||||||
"github.com/tendermint/tendermint/crypto/xsalsa20symmetric",
|
"github.com/tendermint/tendermint/crypto/xsalsa20symmetric",
|
||||||
"github.com/tendermint/tendermint/libs/autofile",
|
|
||||||
"github.com/tendermint/tendermint/libs/bech32",
|
"github.com/tendermint/tendermint/libs/bech32",
|
||||||
"github.com/tendermint/tendermint/libs/cli",
|
"github.com/tendermint/tendermint/libs/cli",
|
||||||
"github.com/tendermint/tendermint/libs/cli/flags",
|
"github.com/tendermint/tendermint/libs/cli/flags",
|
||||||
|
|
45
Makefile
45
Makefile
|
@ -14,7 +14,8 @@ GOTOOLS = \
|
||||||
github.com/alecthomas/gometalinter \
|
github.com/alecthomas/gometalinter \
|
||||||
github.com/rakyll/statik
|
github.com/rakyll/statik
|
||||||
GOBIN ?= $(GOPATH)/bin
|
GOBIN ?= $(GOPATH)/bin
|
||||||
all: devtools vendor-deps install install_examples install_cosmos-sdk-cli test_lint test
|
|
||||||
|
all: devtools get_vendor_deps install test_lint test
|
||||||
|
|
||||||
# The below include contains the tools target.
|
# The below include contains the tools target.
|
||||||
include scripts/Makefile
|
include scripts/Makefile
|
||||||
|
@ -54,13 +55,11 @@ build:
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad
|
go build $(BUILD_FLAGS) -o build/gaiad.exe ./cmd/gaia/cmd/gaiad
|
||||||
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli
|
go build $(BUILD_FLAGS) -o build/gaiacli.exe ./cmd/gaia/cmd/gaiacli
|
||||||
go build $(BUILD_FLAGS) -o build/logjack ./cmd/logjack
|
|
||||||
else
|
else
|
||||||
go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaia/cmd/gaiad
|
go build $(BUILD_FLAGS) -o build/gaiad ./cmd/gaia/cmd/gaiad
|
||||||
go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaia/cmd/gaiacli
|
go build $(BUILD_FLAGS) -o build/gaiacli ./cmd/gaia/cmd/gaiacli
|
||||||
go build $(BUILD_FLAGS) -o build/gaiareplay ./cmd/gaia/cmd/gaiareplay
|
go build $(BUILD_FLAGS) -o build/gaiareplay ./cmd/gaia/cmd/gaiareplay
|
||||||
go build $(BUILD_FLAGS) -o build/gaiakeyutil ./cmd/gaia/cmd/gaiakeyutil
|
go build $(BUILD_FLAGS) -o build/gaiakeyutil ./cmd/gaia/cmd/gaiakeyutil
|
||||||
go build $(BUILD_FLAGS) -o build/logjack ./cmd/logjack
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
build-linux:
|
build-linux:
|
||||||
|
@ -69,41 +68,11 @@ build-linux:
|
||||||
update_gaia_lite_docs:
|
update_gaia_lite_docs:
|
||||||
@statik -src=client/lcd/swagger-ui -dest=client/lcd -f
|
@statik -src=client/lcd/swagger-ui -dest=client/lcd -f
|
||||||
|
|
||||||
build_cosmos-sdk-cli:
|
|
||||||
ifeq ($(OS),Windows_NT)
|
|
||||||
go build $(BUILD_FLAGS) -o build/cosmos-sdk-cli.exe ./cmd/cosmos-sdk-cli
|
|
||||||
else
|
|
||||||
go build $(BUILD_FLAGS) -o build/cosmos-sdk-cli ./cmd/cosmos-sdk-cli
|
|
||||||
endif
|
|
||||||
|
|
||||||
build_examples:
|
|
||||||
ifeq ($(OS),Windows_NT)
|
|
||||||
go build $(BUILD_FLAGS) -o build/basecoind.exe ./docs/examples/basecoin/cmd/basecoind
|
|
||||||
go build $(BUILD_FLAGS) -o build/basecli.exe ./docs/examples/basecoin/cmd/basecli
|
|
||||||
go build $(BUILD_FLAGS) -o build/democoind.exe ./docs/examples/democoin/cmd/democoind
|
|
||||||
go build $(BUILD_FLAGS) -o build/democli.exe ./docs/examples/democoin/cmd/democli
|
|
||||||
else
|
|
||||||
go build $(BUILD_FLAGS) -o build/basecoind ./docs/examples/basecoin/cmd/basecoind
|
|
||||||
go build $(BUILD_FLAGS) -o build/basecli ./docs/examples/basecoin/cmd/basecli
|
|
||||||
go build $(BUILD_FLAGS) -o build/democoind ./docs/examples/democoin/cmd/democoind
|
|
||||||
go build $(BUILD_FLAGS) -o build/democli ./docs/examples/democoin/cmd/democli
|
|
||||||
endif
|
|
||||||
|
|
||||||
install: check-ledger update_gaia_lite_docs
|
install: check-ledger update_gaia_lite_docs
|
||||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad
|
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad
|
||||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli
|
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli
|
||||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiareplay
|
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiareplay
|
||||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiakeyutil
|
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiakeyutil
|
||||||
go install $(BUILD_FLAGS) ./cmd/logjack
|
|
||||||
|
|
||||||
install_examples:
|
|
||||||
go install $(BUILD_FLAGS) ./docs/examples/basecoin/cmd/basecoind
|
|
||||||
go install $(BUILD_FLAGS) ./docs/examples/basecoin/cmd/basecli
|
|
||||||
go install $(BUILD_FLAGS) ./docs/examples/democoin/cmd/democoind
|
|
||||||
go install $(BUILD_FLAGS) ./docs/examples/democoin/cmd/democli
|
|
||||||
|
|
||||||
install_cosmos-sdk-cli:
|
|
||||||
go install $(BUILD_FLAGS) ./cmd/cosmos-sdk-cli
|
|
||||||
|
|
||||||
install_debug:
|
install_debug:
|
||||||
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiadebug
|
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiadebug
|
||||||
|
@ -171,10 +140,6 @@ test: test_unit
|
||||||
test_cli:
|
test_cli:
|
||||||
@go test -p 4 `go list github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test` -tags=cli_test
|
@go test -p 4 `go list github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test` -tags=cli_test
|
||||||
|
|
||||||
test_examples:
|
|
||||||
@go test -count 1 -p 1 `go list github.com/cosmos/cosmos-sdk/docs/examples/basecoin/cli_test` -tags=cli_test
|
|
||||||
@go test -count 1 -p 1 `go list github.com/cosmos/cosmos-sdk/docs/examples/democoin/cli_test` -tags=cli_test
|
|
||||||
|
|
||||||
test_unit:
|
test_unit:
|
||||||
@VERSION=$(VERSION) go test $(PACKAGES_NOSIMULATION)
|
@VERSION=$(VERSION) go test $(PACKAGES_NOSIMULATION)
|
||||||
|
|
||||||
|
@ -217,7 +182,7 @@ test_cover:
|
||||||
|
|
||||||
test_lint:
|
test_lint:
|
||||||
gometalinter --config=tools/gometalinter.json ./...
|
gometalinter --config=tools/gometalinter.json ./...
|
||||||
!(gometalinter --exclude /usr/lib/go/src/ --exclude client/lcd/statik/statik.go --exclude 'vendor/*' --exclude 'cmd/logjack/*' --disable-all --enable='errcheck' --vendor ./... | grep -v "client/")
|
!(gometalinter --exclude /usr/lib/go/src/ --exclude client/lcd/statik/statik.go --exclude 'vendor/*' --disable-all --enable='errcheck' --vendor ./... | grep -v "client/")
|
||||||
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s
|
find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s
|
||||||
dep status >> /dev/null
|
dep status >> /dev/null
|
||||||
!(grep -n branch Gopkg.toml)
|
!(grep -n branch Gopkg.toml)
|
||||||
|
@ -273,8 +238,8 @@ localnet-stop:
|
||||||
# To avoid unintended conflicts with file names, always add to .PHONY
|
# To avoid unintended conflicts with file names, always add to .PHONY
|
||||||
# unless there is a reason not to.
|
# unless there is a reason not to.
|
||||||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
||||||
.PHONY: build build_cosmos-sdk-cli build_examples install install_examples install_cosmos-sdk-cli install_debug dist \
|
.PHONY: build install install_debug dist \
|
||||||
check_tools check_dev_tools draw_deps test test_cli test_unit \
|
check_tools check_dev_tools get_vendor_deps draw_deps test test_cli test_unit \
|
||||||
test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \
|
test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \
|
||||||
build-linux build-docker-gaiadnode localnet-start localnet-stop \
|
build-linux build-docker-gaiadnode localnet-start localnet-stop \
|
||||||
format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \
|
format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \
|
||||||
|
|
|
@ -4,7 +4,5 @@ functionality and act as a bridge between the ABCI interface and the SDK
|
||||||
abstractions.
|
abstractions.
|
||||||
|
|
||||||
BaseApp has no state except the CommitMultiStore you provide upon init.
|
BaseApp has no state except the CommitMultiStore you provide upon init.
|
||||||
|
|
||||||
See examples/basecoin/app/* for usage.
|
|
||||||
*/
|
*/
|
||||||
package baseapp
|
package baseapp
|
||||||
|
|
|
@ -23,7 +23,7 @@ func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) {
|
||||||
return app.runTx(runTxModeDeliver, nil, tx)
|
return app.runTx(runTxModeDeliver, nil, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunForever - BasecoinApp execution and cleanup
|
// RunForever BasecoinApp execution and cleanup
|
||||||
func RunForever(app abci.Application) {
|
func RunForever(app abci.Application) {
|
||||||
|
|
||||||
// Start the ABCI server
|
// Start the ABCI server
|
||||||
|
|
|
@ -104,7 +104,7 @@ func (ctx CLIContext) broadcastTxAsync(txBytes []byte) (*ctypes.ResultBroadcastT
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Output != nil {
|
if ctx.Output != nil {
|
||||||
if ctx.JSON {
|
if ctx.OutputFormat == "json" {
|
||||||
type toJSON struct {
|
type toJSON struct {
|
||||||
TxHash string
|
TxHash string
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ func (ctx CLIContext) broadcastTxCommit(txBytes []byte) (*ctypes.ResultBroadcast
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.JSON {
|
if ctx.OutputFormat == "json" {
|
||||||
// Since JSON is intended for automated scripts, always include response in
|
// Since JSON is intended for automated scripts, always include response in
|
||||||
// JSON mode.
|
// JSON mode.
|
||||||
type toJSON struct {
|
type toJSON struct {
|
||||||
|
|
|
@ -43,7 +43,6 @@ type CLIContext struct {
|
||||||
TrustNode bool
|
TrustNode bool
|
||||||
UseLedger bool
|
UseLedger bool
|
||||||
Async bool
|
Async bool
|
||||||
JSON bool
|
|
||||||
PrintResponse bool
|
PrintResponse bool
|
||||||
Verifier tmlite.Verifier
|
Verifier tmlite.Verifier
|
||||||
Simulate bool
|
Simulate bool
|
||||||
|
@ -82,7 +81,6 @@ func NewCLIContext() CLIContext {
|
||||||
TrustNode: viper.GetBool(client.FlagTrustNode),
|
TrustNode: viper.GetBool(client.FlagTrustNode),
|
||||||
UseLedger: viper.GetBool(client.FlagUseLedger),
|
UseLedger: viper.GetBool(client.FlagUseLedger),
|
||||||
Async: viper.GetBool(client.FlagAsync),
|
Async: viper.GetBool(client.FlagAsync),
|
||||||
JSON: viper.GetBool(client.FlagJson),
|
|
||||||
PrintResponse: viper.GetBool(client.FlagPrintResponse),
|
PrintResponse: viper.GetBool(client.FlagPrintResponse),
|
||||||
Verifier: verifier,
|
Verifier: verifier,
|
||||||
Simulate: viper.GetBool(client.FlagDryRun),
|
Simulate: viper.GetBool(client.FlagDryRun),
|
||||||
|
|
|
@ -33,7 +33,6 @@ const (
|
||||||
FlagFees = "fees"
|
FlagFees = "fees"
|
||||||
FlagGasPrices = "gas-prices"
|
FlagGasPrices = "gas-prices"
|
||||||
FlagAsync = "async"
|
FlagAsync = "async"
|
||||||
FlagJson = "json"
|
|
||||||
FlagPrintResponse = "print-response"
|
FlagPrintResponse = "print-response"
|
||||||
FlagDryRun = "dry-run"
|
FlagDryRun = "dry-run"
|
||||||
FlagGenerateOnly = "generate-only"
|
FlagGenerateOnly = "generate-only"
|
||||||
|
@ -86,7 +85,6 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||||
c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
|
c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
|
||||||
c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ")
|
c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ")
|
||||||
c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously")
|
c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously")
|
||||||
c.Flags().Bool(FlagJson, false, "return output in json format")
|
|
||||||
c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)")
|
c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)")
|
||||||
c.Flags().Bool(FlagTrustNode, true, "Trust connected full node (don't verify proofs for responses)")
|
c.Flags().Bool(FlagTrustNode, true, "Trust connected full node (don't verify proofs for responses)")
|
||||||
c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it")
|
c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it")
|
||||||
|
|
|
@ -1,162 +0,0 @@
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"go/build"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
tmversion "github.com/tendermint/tendermint/version"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/version"
|
|
||||||
)
|
|
||||||
|
|
||||||
var remoteBasecoinPath = "github.com/cosmos/cosmos-sdk/docs/examples/basecoin"
|
|
||||||
|
|
||||||
// Replacer to replace all instances of basecoin/basecli/BasecoinApp to project specific names
|
|
||||||
// Gets initialized when initCmd is executing after getting the project name from user
|
|
||||||
var replacer *strings.Replacer
|
|
||||||
|
|
||||||
// Remote path for the project.
|
|
||||||
var remoteProjectPath string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
initCmd.Flags().StringVarP(&remoteProjectPath, "project-path", "p", "", "Remote project path. eg: github.com/your_user_name/project_name")
|
|
||||||
rootCmd.AddCommand(initCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
var initCmd = &cobra.Command{
|
|
||||||
Use: "init [ProjectName]",
|
|
||||||
Short: "Initialize your new cosmos zone",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
fmt.Print("Thanks for choosing Cosmos-SDK to build your project.\n\n")
|
|
||||||
projectName := args[0]
|
|
||||||
capitalizedProjectName := strings.Title(projectName)
|
|
||||||
shortProjectName := strings.ToLower(projectName)
|
|
||||||
remoteProjectPath = strings.ToLower(strings.TrimSpace(remoteProjectPath))
|
|
||||||
if remoteProjectPath == "" {
|
|
||||||
remoteProjectPath = strings.ToLower(shortProjectName)
|
|
||||||
}
|
|
||||||
replacer = strings.NewReplacer("basecli", shortProjectName+"cli",
|
|
||||||
"basecoind", shortProjectName+"d",
|
|
||||||
"BasecoinApp", capitalizedProjectName+"App",
|
|
||||||
remoteBasecoinPath, remoteProjectPath,
|
|
||||||
"basecoin", shortProjectName,
|
|
||||||
"Basecoin", capitalizedProjectName)
|
|
||||||
return setupBasecoinWorkspace(shortProjectName, remoteProjectPath)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveProjectPath(remoteProjectPath string) string {
|
|
||||||
gopath := os.Getenv("GOPATH")
|
|
||||||
if gopath == "" {
|
|
||||||
gopath = build.Default.GOPATH
|
|
||||||
// Use $HOME/go
|
|
||||||
}
|
|
||||||
return gopath + string(os.PathSeparator) + "src" + string(os.PathSeparator) + remoteProjectPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint: unparam, errcheck
|
|
||||||
func copyBasecoinTemplate(projectName string, projectPath string, remoteProjectPath string) {
|
|
||||||
basecoinProjectPath := resolveProjectPath(remoteBasecoinPath)
|
|
||||||
filepath.Walk(basecoinProjectPath, func(path string, f os.FileInfo, err error) error {
|
|
||||||
if !f.IsDir() {
|
|
||||||
data, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
contents := string(data)
|
|
||||||
// Extract relative file path eg: app/app.go instead of /Users/..../github.com/cosmos/...examples/basecoin/app/app.go
|
|
||||||
relativeFilePath := path[len(basecoinProjectPath)+1:]
|
|
||||||
// Evaluating the filepath in the new project folder
|
|
||||||
projectFilePath := projectPath + string(os.PathSeparator) + relativeFilePath
|
|
||||||
projectFilePath = replacer.Replace(projectFilePath)
|
|
||||||
lengthOfRootDir := strings.LastIndex(projectFilePath, string(os.PathSeparator))
|
|
||||||
// Extracting the path of root directory from the filepath
|
|
||||||
rootDir := projectFilePath[0:lengthOfRootDir]
|
|
||||||
// Creating the required directory first
|
|
||||||
os.MkdirAll(rootDir, os.ModePerm)
|
|
||||||
fmt.Println("Creating " + projectFilePath)
|
|
||||||
// Writing the contents to a file in the project folder
|
|
||||||
contents = replacer.Replace(contents)
|
|
||||||
ioutil.WriteFile(projectFilePath, []byte(contents), os.ModePerm)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint: errcheck
|
|
||||||
func createGopkg(projectPath string) {
|
|
||||||
// Create gopkg.toml file
|
|
||||||
dependencies := map[string]string{
|
|
||||||
"github.com/cosmos/cosmos-sdk": "=" + version.Version,
|
|
||||||
"github.com/stretchr/testify": "=1.2.1",
|
|
||||||
"github.com/spf13/cobra": "=0.0.1",
|
|
||||||
"github.com/spf13/viper": "=1.0.0",
|
|
||||||
}
|
|
||||||
overrides := map[string]string{
|
|
||||||
"github.com/golang/protobuf": "1.1.0",
|
|
||||||
"github.com/tendermint/tendermint": tmversion.Version,
|
|
||||||
}
|
|
||||||
contents := ""
|
|
||||||
for dependency, version := range dependencies {
|
|
||||||
contents += "[[constraint]]\n\tname = \"" + dependency + "\"\n\tversion = \"" + version + "\"\n\n"
|
|
||||||
}
|
|
||||||
for dependency, version := range overrides {
|
|
||||||
contents += "[[override]]\n\tname = \"" + dependency + "\"\n\tversion = \"=" + version + "\"\n\n"
|
|
||||||
}
|
|
||||||
contents += "[prune]\n\tgo-tests = true\n\tunused-packages = true"
|
|
||||||
ioutil.WriteFile(projectPath+"/Gopkg.toml", []byte(contents), os.ModePerm)
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint: errcheck
|
|
||||||
func createMakefile(projectPath string) {
|
|
||||||
// Create makefile
|
|
||||||
// TODO: Should we use tools/ directory as in Cosmos-SDK to get tools for linting etc.
|
|
||||||
makefileContents := `PACKAGES=$(shell go list ./... | grep -v '/vendor/')
|
|
||||||
|
|
||||||
all: get_tools get_vendor_deps build test
|
|
||||||
|
|
||||||
get_tools:
|
|
||||||
go get github.com/golang/dep/cmd/dep
|
|
||||||
|
|
||||||
build:
|
|
||||||
go build -o bin/basecli cmd/basecli/main.go && go build -o bin/basecoind cmd/basecoind/main.go
|
|
||||||
|
|
||||||
get_vendor_deps:
|
|
||||||
@rm -rf vendor/
|
|
||||||
@dep ensure
|
|
||||||
|
|
||||||
test:
|
|
||||||
@go test $(PACKAGES)
|
|
||||||
|
|
||||||
benchmark:
|
|
||||||
@go test -bench=. $(PACKAGES)
|
|
||||||
|
|
||||||
.PHONY: all build test benchmark`
|
|
||||||
|
|
||||||
// Replacing instances of base* to project specific names
|
|
||||||
makefileContents = replacer.Replace(makefileContents)
|
|
||||||
|
|
||||||
ioutil.WriteFile(projectPath+"/Makefile", []byte(makefileContents), os.ModePerm)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupBasecoinWorkspace(projectName string, remoteProjectPath string) error {
|
|
||||||
projectPath := resolveProjectPath(remoteProjectPath)
|
|
||||||
fmt.Println("Configuring your project in " + projectPath)
|
|
||||||
// Check if the projectPath already exists or not
|
|
||||||
if _, err := os.Stat(projectPath); !os.IsNotExist(err) {
|
|
||||||
return fmt.Errorf("Unable to initialize the project. %s already exists", projectPath)
|
|
||||||
}
|
|
||||||
copyBasecoinTemplate(projectName, projectPath, remoteProjectPath)
|
|
||||||
createGopkg(projectPath)
|
|
||||||
createMakefile(projectPath)
|
|
||||||
fmt.Printf("Initialized a new project at %s.\nHappy hacking!\n", projectPath)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
|
||||||
Use: "cosmos-sdk-cli",
|
|
||||||
Short: "Tools to develop on cosmos-sdk",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the command
|
|
||||||
func Execute() {
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cosmos/cosmos-sdk/cmd/cosmos-sdk-cli/cmd"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
cmd.Execute()
|
|
||||||
}
|
|
|
@ -52,7 +52,7 @@ func TestGaiaCLIMinimumFees(t *testing.T) {
|
||||||
// start gaiad server with minimum fees
|
// start gaiad server with minimum fees
|
||||||
minGasPrice, _ := sdk.NewDecFromStr("0.000006")
|
minGasPrice, _ := sdk.NewDecFromStr("0.000006")
|
||||||
fees := fmt.Sprintf(
|
fees := fmt.Sprintf(
|
||||||
"--minimum_gas_prices=%s,%s",
|
"--minimum-gas-prices=%s,%s",
|
||||||
sdk.NewDecCoinFromDec(feeDenom, minGasPrice),
|
sdk.NewDecCoinFromDec(feeDenom, minGasPrice),
|
||||||
sdk.NewDecCoinFromDec(fee2Denom, minGasPrice),
|
sdk.NewDecCoinFromDec(fee2Denom, minGasPrice),
|
||||||
)
|
)
|
||||||
|
@ -87,7 +87,7 @@ func TestGaiaCLIGasPrices(t *testing.T) {
|
||||||
|
|
||||||
// start gaiad server with minimum fees
|
// start gaiad server with minimum fees
|
||||||
minGasPrice, _ := sdk.NewDecFromStr("0.000006")
|
minGasPrice, _ := sdk.NewDecFromStr("0.000006")
|
||||||
proc := f.GDStart(fmt.Sprintf("--minimum_gas_prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice)))
|
proc := f.GDStart(fmt.Sprintf("--minimum-gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice)))
|
||||||
defer proc.Stop(false)
|
defer proc.Stop(false)
|
||||||
|
|
||||||
barAddr := f.KeyAddress(keyBar)
|
barAddr := f.KeyAddress(keyBar)
|
||||||
|
@ -120,7 +120,7 @@ func TestGaiaCLIFeesDeduction(t *testing.T) {
|
||||||
|
|
||||||
// start gaiad server with minimum fees
|
// start gaiad server with minimum fees
|
||||||
minGasPrice, _ := sdk.NewDecFromStr("0.000006")
|
minGasPrice, _ := sdk.NewDecFromStr("0.000006")
|
||||||
proc := f.GDStart(fmt.Sprintf("--minimum_gas_prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice)))
|
proc := f.GDStart(fmt.Sprintf("--minimum-gas-prices=%s", sdk.NewDecCoinFromDec(feeDenom, minGasPrice)))
|
||||||
defer proc.Stop(false)
|
defer proc.Stop(false)
|
||||||
|
|
||||||
// Save key addresses for later use
|
// Save key addresses for later use
|
||||||
|
@ -260,7 +260,7 @@ func TestGaiaCLIGasAuto(t *testing.T) {
|
||||||
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(denom).Int64())
|
require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(denom).Int64())
|
||||||
|
|
||||||
// Enable auto gas
|
// Enable auto gas
|
||||||
success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--gas=auto", "--json")
|
success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--gas=auto")
|
||||||
require.NotEmpty(t, stderr)
|
require.NotEmpty(t, stderr)
|
||||||
require.True(t, success)
|
require.True(t, success)
|
||||||
cdc := app.MakeCodec()
|
cdc := app.MakeCodec()
|
||||||
|
@ -646,7 +646,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
|
||||||
defer os.Remove(unsignedTxFile.Name())
|
defer os.Remove(unsignedTxFile.Name())
|
||||||
|
|
||||||
// Test sign --validate-signatures
|
// Test sign --validate-signatures
|
||||||
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--validate-signatures", "--json")
|
success, stdout, _ = f.TxSign(keyFoo, unsignedTxFile.Name(), "--validate-signatures")
|
||||||
require.False(t, success)
|
require.False(t, success)
|
||||||
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", fooAddr.String()), stdout)
|
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n\n", fooAddr.String()), stdout)
|
||||||
|
|
||||||
|
@ -663,7 +663,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
|
||||||
defer os.Remove(signedTxFile.Name())
|
defer os.Remove(signedTxFile.Name())
|
||||||
|
|
||||||
// Test sign --validate-signatures
|
// Test sign --validate-signatures
|
||||||
success, stdout, _ = f.TxSign(keyFoo, signedTxFile.Name(), "--validate-signatures", "--json")
|
success, stdout, _ = f.TxSign(keyFoo, signedTxFile.Name(), "--validate-signatures")
|
||||||
require.True(t, success)
|
require.True(t, success)
|
||||||
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\t[OK]\n\n", fooAddr.String(),
|
require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\t[OK]\n\n", fooAddr.String(),
|
||||||
fooAddr.String()), stdout)
|
fooAddr.String()), stdout)
|
||||||
|
@ -736,7 +736,7 @@ func TestGaiaCLIMultisignInsufficientCosigners(t *testing.T) {
|
||||||
defer os.Remove(signedTxFile.Name())
|
defer os.Remove(signedTxFile.Name())
|
||||||
|
|
||||||
// Validate the multisignature
|
// Validate the multisignature
|
||||||
success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures", "--json")
|
success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures")
|
||||||
require.False(t, success)
|
require.False(t, success)
|
||||||
|
|
||||||
// Broadcast the transaction
|
// Broadcast the transaction
|
||||||
|
@ -798,7 +798,7 @@ func TestGaiaCLIMultisignSortSignatures(t *testing.T) {
|
||||||
defer os.Remove(signedTxFile.Name())
|
defer os.Remove(signedTxFile.Name())
|
||||||
|
|
||||||
// Validate the multisignature
|
// Validate the multisignature
|
||||||
success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures", "--json")
|
success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures")
|
||||||
require.True(t, success)
|
require.True(t, success)
|
||||||
|
|
||||||
// Broadcast the transaction
|
// Broadcast the transaction
|
||||||
|
@ -861,7 +861,7 @@ func TestGaiaCLIMultisign(t *testing.T) {
|
||||||
defer os.Remove(signedTxFile.Name())
|
defer os.Remove(signedTxFile.Name())
|
||||||
|
|
||||||
// Validate the multisignature
|
// Validate the multisignature
|
||||||
success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures", "--json")
|
success, _, _ = f.TxSign(keyFooBarBaz, signedTxFile.Name(), "--validate-signatures")
|
||||||
require.True(t, success)
|
require.True(t, success)
|
||||||
|
|
||||||
// Broadcast the transaction
|
// Broadcast the transaction
|
||||||
|
|
|
@ -238,7 +238,7 @@ func (f *Fixtures) TxSign(signer, fileName string, flags ...string) (bool, strin
|
||||||
|
|
||||||
// TxBroadcast is gaiacli tx sign
|
// TxBroadcast is gaiacli tx sign
|
||||||
func (f *Fixtures) TxBroadcast(fileName string, flags ...string) (bool, string, string) {
|
func (f *Fixtures) TxBroadcast(fileName string, flags ...string) (bool, string, string) {
|
||||||
cmd := fmt.Sprintf("gaiacli tx broadcast %v --json %v", f.Flags(), fileName)
|
cmd := fmt.Sprintf("gaiacli tx broadcast %v %v", f.Flags(), fileName)
|
||||||
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
// nolint
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
auto "github.com/tendermint/tendermint/libs/autofile"
|
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
//nolint
|
|
||||||
const Version = "0.0.2"
|
|
||||||
const sleepSeconds = 1 // Every second
|
|
||||||
const readBufferSize = 1024 // 1KB at a time
|
|
||||||
|
|
||||||
// Parse command-line options
|
|
||||||
func parseFlags() (headPath string, chopSize int64, limitSize int64, version bool) {
|
|
||||||
var flagSet = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
|
||||||
var chopSizeStr, limitSizeStr string
|
|
||||||
flagSet.StringVar(&headPath, "head", "logjack.out", "Destination (head) file.")
|
|
||||||
flagSet.StringVar(&chopSizeStr, "chop", "100M", "Move file if greater than this")
|
|
||||||
flagSet.StringVar(&limitSizeStr, "limit", "10G", "Only keep this much (for each specified file). Remove old files.")
|
|
||||||
flagSet.BoolVar(&version, "version", false, "Version")
|
|
||||||
flagSet.Parse(os.Args[1:]) //nolint
|
|
||||||
chopSize = parseBytesize(chopSizeStr)
|
|
||||||
limitSize = parseBytesize(limitSizeStr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// Read options
|
|
||||||
headPath, chopSize, limitSize, version := parseFlags()
|
|
||||||
if version {
|
|
||||||
fmt.Printf("logjack version %v\n", Version)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open Group
|
|
||||||
group, err := auto.OpenGroup(headPath, auto.GroupHeadSizeLimit(chopSize), auto.GroupTotalSizeLimit(limitSize))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("logjack couldn't create output file %v\n", headPath)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
// TODO: Maybe fix Group to re-allow these mutations.
|
|
||||||
// group.SetHeadSizeLimit(chopSize)
|
|
||||||
// group.SetTotalSizeLimit(limitSize)
|
|
||||||
err = group.Start()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("logjack couldn't start with file %v\n", headPath)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
// Forever, read from stdin and write to AutoFile.
|
|
||||||
buf := make([]byte, readBufferSize)
|
|
||||||
for {
|
|
||||||
n, err := os.Stdin.Read(buf)
|
|
||||||
group.Write(buf[:n]) //nolint
|
|
||||||
group.Flush() //nolint
|
|
||||||
if err != nil {
|
|
||||||
group.Stop() //nolint
|
|
||||||
if err == io.EOF {
|
|
||||||
os.Exit(0)
|
|
||||||
} else {
|
|
||||||
fmt.Println("logjack errored")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Trap signal
|
|
||||||
cmn.TrapSignal(func() {
|
|
||||||
fmt.Println("logjack shutting down")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseBytesize(chopSize string) int64 {
|
|
||||||
// Handle suffix multiplier
|
|
||||||
var multiplier int64 = 1
|
|
||||||
if strings.HasSuffix(chopSize, "T") {
|
|
||||||
multiplier = 1042 * 1024 * 1024 * 1024
|
|
||||||
chopSize = chopSize[:len(chopSize)-1]
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(chopSize, "G") {
|
|
||||||
multiplier = 1042 * 1024 * 1024
|
|
||||||
chopSize = chopSize[:len(chopSize)-1]
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(chopSize, "M") {
|
|
||||||
multiplier = 1042 * 1024
|
|
||||||
chopSize = chopSize[:len(chopSize)-1]
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(chopSize, "K") {
|
|
||||||
multiplier = 1042
|
|
||||||
chopSize = chopSize[:len(chopSize)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the numeric part
|
|
||||||
chopSizeInt, err := strconv.Atoi(chopSize)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return int64(chopSizeInt) * multiplier
|
|
||||||
}
|
|
|
@ -74,7 +74,6 @@ The default implementation of `Account` is the `BaseAccount`:
|
||||||
```go
|
```go
|
||||||
// BaseAccount - base account structure.
|
// BaseAccount - base account structure.
|
||||||
// Extend this by embedding this in your AppAccount.
|
// Extend this by embedding this in your AppAccount.
|
||||||
// See the examples/basecoin/types/account.go for an example.
|
|
||||||
type BaseAccount struct {
|
type BaseAccount struct {
|
||||||
Address sdk.AccAddress `json:"address"`
|
Address sdk.AccAddress `json:"address"`
|
||||||
Coins sdk.Coins `json:"coins"`
|
Coins sdk.Coins `json:"coins"`
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
There is a need for a scalable structure of the SDK documentation. Current documentation includes a lot of non-related SDK material, is difficult to maintain and hard to follow as a user.
|
There is a need for a scalable structure of the SDK documentation. Current documentation includes a lot of non-related SDK material, is difficult to maintain and hard to follow as a user.
|
||||||
|
|
||||||
Ideally, we would have:
|
Ideally, we would have:
|
||||||
- All docs related to dev frameworks or tools live in their respective github repos (sdk repo would contain sdk docs, hub repo would contain hub docs, lotion repo would contain lotion docs, etc.)
|
- All docs related to dev frameworks or tools live in their respective github repos (sdk repo would contain sdk docs, hub repo would contain hub docs, lotion repo would contain lotion docs, etc.)
|
||||||
|
@ -34,9 +34,6 @@ docs/
|
||||||
│ │ ├── cli
|
│ │ ├── cli
|
||||||
│ ├── gas
|
│ ├── gas
|
||||||
│ └── commands
|
│ └── commands
|
||||||
├── examples/
|
|
||||||
│ ├── basecoin/
|
|
||||||
│ └── democoin/
|
|
||||||
├── clients/
|
├── clients/
|
||||||
│ ├── lite/
|
│ ├── lite/
|
||||||
│ ├── service-providers
|
│ ├── service-providers
|
||||||
|
@ -46,11 +43,10 @@ docs/
|
||||||
|
|
||||||
The files in each sub-folders do not matter and will likely change. What matters is the sectioning:
|
The files in each sub-folders do not matter and will likely change. What matters is the sectioning:
|
||||||
|
|
||||||
- `README`: Landing page of the docs.
|
- `README`: Landing page of the docs.
|
||||||
- `into`: Introductory material. Goal is to have a short explainer of the SDK and then channel people to the resource they need. The [sdk-tutorial](https://github.com/cosmos/sdk-application-tutorial/) will be highlighted, as well as the `godocs`.
|
- `into`: Introductory material. Goal is to have a short explainer of the SDK and then channel people to the resource they need. The [sdk-tutorial](https://github.com/cosmos/sdk-application-tutorial/) will be highlighted, as well as the `godocs`.
|
||||||
- `gaia`: Contains all docs related to the `gaia` application. Will later be renamed to `cosmos-hub` or `chub` and probably moved to its own repository.
|
- `gaia`: Contains all docs related to the `gaia` application. Will later be renamed to `cosmos-hub` or `chub` and probably moved to its own repository.
|
||||||
- `concepts`: Contains high-level explanations of the abstractions of the SDK. It does not contain specific code implementation and does not need to be updated often. **It is not an API specification of the interfaces**. API spec is the `godoc`.
|
- `concepts`: Contains high-level explanations of the abstractions of the SDK. It does not contain specific code implementation and does not need to be updated often. **It is not an API specification of the interfaces**. API spec is the `godoc`.
|
||||||
- `examples`: Contain a couple examples of sdk application like `basecoin` and `democoin`. Developers need to maintain them up-to-date and make sure they compile as the SDK gets upgraded.
|
|
||||||
- `clients`: Contains specs and info about the various SDK clients.
|
- `clients`: Contains specs and info about the various SDK clients.
|
||||||
- `spec`: Contains specs of modules, and others.
|
- `spec`: Contains specs of modules, and others.
|
||||||
- `architecture`: Contains architecture-related docs like the present one.
|
- `architecture`: Contains architecture-related docs like the present one.
|
||||||
|
@ -73,16 +69,16 @@ Accepted
|
||||||
|
|
||||||
### Positive
|
### Positive
|
||||||
|
|
||||||
- Much clearer organisation of the SDK docs.
|
- Much clearer organisation of the SDK docs.
|
||||||
- The `/docs` folder now only contains SDK and gaia related material. Later, it will only contain SDK related material.
|
- The `/docs` folder now only contains SDK and gaia related material. Later, it will only contain SDK related material.
|
||||||
- Developers only have to update `/docs` folder when they open a PR (and not `/examples` for example).
|
- Developers only have to update `/docs` folder when they open a PR (and not `/examples` for example).
|
||||||
- Easier for developers to find what they need to update in the docs thanks to reworked architecture.
|
- Easier for developers to find what they need to update in the docs thanks to reworked architecture.
|
||||||
- Cleaner vuepress build for website docs.
|
- Cleaner vuepress build for website docs.
|
||||||
- Will help build an executable doc (cf https://github.com/cosmos/cosmos-sdk/issues/2611)
|
- Will help build an executable doc (cf https://github.com/cosmos/cosmos-sdk/issues/2611)
|
||||||
|
|
||||||
### Neutral
|
### Neutral
|
||||||
|
|
||||||
- We need to move a bunch of deprecated stuff to `/_attic` folder.
|
- We need to move a bunch of deprecated stuff to `/_attic` folder.
|
||||||
- We need to integrate content in `docs/sdk/docs/core` in `concepts`.
|
- We need to integrate content in `docs/sdk/docs/core` in `concepts`.
|
||||||
- We need to move all the content that currently lives in `docs` and does not fit in new structure (like `lotion`, intro material, whitepaper) to the website repository.
|
- We need to move all the content that currently lives in `docs` and does not fit in new structure (like `lotion`, intro material, whitepaper) to the website repository.
|
||||||
- Update `DOCS_README.md`
|
- Update `DOCS_README.md`
|
||||||
|
@ -91,4 +87,4 @@ Accepted
|
||||||
|
|
||||||
- https://github.com/cosmos/cosmos-sdk/issues/1460
|
- https://github.com/cosmos/cosmos-sdk/issues/1460
|
||||||
- https://github.com/cosmos/cosmos-sdk/pull/2695
|
- https://github.com/cosmos/cosmos-sdk/pull/2695
|
||||||
- https://github.com/cosmos/cosmos-sdk/issues/2611
|
- https://github.com/cosmos/cosmos-sdk/issues/2611
|
||||||
|
|
|
@ -1,320 +0,0 @@
|
||||||
# Basecoin Example
|
|
||||||
|
|
||||||
Here we explain how to get started with a basic Basecoin blockchain, how
|
|
||||||
to send transactions between accounts using the ``basecli`` tool, and
|
|
||||||
what is happening under the hood.
|
|
||||||
|
|
||||||
## Setup and Install
|
|
||||||
|
|
||||||
You will need to have go installed on your computer. Please refer to the [cosmos testnet tutorial](https://cosmos.network/validators/tutorial), which will always have the most updated instructions on how to get setup with go and the cosmos repository.
|
|
||||||
|
|
||||||
Once you have go installed, run the command:
|
|
||||||
|
|
||||||
```
|
|
||||||
go get github.com/cosmos/cosmos-sdk
|
|
||||||
```
|
|
||||||
|
|
||||||
There will be an error stating `can't load package: package github.com/cosmos/cosmos-sdk: no Go files`, however you can ignore this error, it doesn't affect us. Now change directories to:
|
|
||||||
|
|
||||||
```
|
|
||||||
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
|
|
||||||
```
|
|
||||||
|
|
||||||
And run :
|
|
||||||
|
|
||||||
```
|
|
||||||
make tools // run make update_tools if you already had it installed
|
|
||||||
make get_vendor_deps
|
|
||||||
make install_examples
|
|
||||||
```
|
|
||||||
Then run `make install_examples`, which creates binaries for `basecli` and `basecoind`. You can look at the Makefile if you want to see the details on what these make commands are doing.
|
|
||||||
|
|
||||||
## Using basecli and basecoind
|
|
||||||
|
|
||||||
Check the versions by running:
|
|
||||||
|
|
||||||
```
|
|
||||||
basecli version
|
|
||||||
basecoind version
|
|
||||||
```
|
|
||||||
|
|
||||||
They should read something like `0.17.1-5d18d5f`, but the versions will be constantly updating so don't worry if your version is higher that 0.17.1. That's a good thing.
|
|
||||||
|
|
||||||
Note that you can always check help in the terminal by running `basecli -h` or `basecoind -h`. It is good to check these out if you are stuck, because updates to the code base might slightly change the commands, and you might find the correct command in there.
|
|
||||||
|
|
||||||
Let's start by initializing the basecoind daemon. Run the command
|
|
||||||
|
|
||||||
```
|
|
||||||
basecoind init
|
|
||||||
```
|
|
||||||
|
|
||||||
And you should see something like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"chain_id": "test-chain-z77iHG",
|
|
||||||
"node_id": "e14c5056212b5736e201dd1d64c89246f3288129",
|
|
||||||
"app_message": {
|
|
||||||
"secret": "pluck life bracket worry guilt wink upgrade olive tilt output reform census member trouble around abandon"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This creates the `~/.basecoind folder`, which has config.toml, genesis.json, node_key.json, priv_validator.json. Take some time to review what is contained in these files if you want to understand what is going on at a deeper level.
|
|
||||||
|
|
||||||
|
|
||||||
## Generating keys
|
|
||||||
|
|
||||||
The next thing we'll need to do is add the key from priv_validator.json to the gaiacli key manager. For this we need the 16 word seed that represents the private key, and a password. You can also get the 16 word seed from the output seen above, under `"secret"`. Then run the command:
|
|
||||||
|
|
||||||
```
|
|
||||||
basecli keys add alice --recover
|
|
||||||
```
|
|
||||||
|
|
||||||
Which will give you three prompts:
|
|
||||||
|
|
||||||
```
|
|
||||||
Enter a passphrase for your key:
|
|
||||||
Repeat the passphrase:
|
|
||||||
Enter your recovery seed phrase:
|
|
||||||
```
|
|
||||||
|
|
||||||
You just created your first locally stored key, under the name alice, and this account is linked to the private key that is running the basecoind validator node. Once you do this, the ~/.basecli folder is created, which will hold the alice key and any other keys you make. Now that you have the key for alice, you can start up the blockchain by running
|
|
||||||
|
|
||||||
```
|
|
||||||
basecoind start
|
|
||||||
```
|
|
||||||
|
|
||||||
You should see blocks being created at a fast rate, with a lot of output in the terminal.
|
|
||||||
|
|
||||||
Next we need to make some more keys so we can use the send transaction functionality of basecoin. Open a new terminal, and run the following commands, to make two new accounts, and give each account a password you can remember:
|
|
||||||
|
|
||||||
```
|
|
||||||
basecli keys add bob
|
|
||||||
basecli keys add charlie
|
|
||||||
```
|
|
||||||
|
|
||||||
You can see your keys with the command:
|
|
||||||
|
|
||||||
```
|
|
||||||
basecli keys list
|
|
||||||
```
|
|
||||||
|
|
||||||
You should now see alice, bob and charlie's account all show up.
|
|
||||||
|
|
||||||
```
|
|
||||||
NAME: ADDRESS: PUBKEY:
|
|
||||||
alice cosmos1khygs0qh7gz3p4m39u00mjhvgvc2dcpxhsuh5f cosmospub1addwnpepq0w037u5g7y7lvdvsred2dehg90j84k0weyss5ynysf0nnnax74agrsxns6
|
|
||||||
bob cosmos18se8tz6kwwfga6k2yjsu7n64e9z52nen29rhzz cosmospub1addwnpepqwe97n8lryxrzvamrvjfj24jys3uzf8wndfvqa2l7mh5nsv4jrvdznvyeg6
|
|
||||||
charlie cosmos13wq5mklhn03ljpd4dkph5rflk5a3ssma2ag07q cosmospub1addwnpepqdmtxv35rrmv2dvcr3yhfyxj7dzrd4z4rnhmclksq4g55a4wpl54clvx33l
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Send transactions
|
|
||||||
|
|
||||||
Lets send bob and charlie some tokens. First, lets query alice's account so we can see what kind of tokens she has:
|
|
||||||
|
|
||||||
```
|
|
||||||
basecli account cosmos1khygs0qh7gz3p4m39u00mjhvgvc2dcpxhsuh5f
|
|
||||||
```
|
|
||||||
|
|
||||||
Where `cosmos1khygs0qh7gz3p4m39u00mjhvgvc2dcpxhsuh5f` is alice's address we got from running `basecli keys list`. You should see a large amount of "mycoin" there. If you search for bob's or charlie's address, the command will fail, because they haven't been added into the blockchain database yet since they have no coins. We need to send them some!
|
|
||||||
|
|
||||||
The following command will send coins from alice, to bob:
|
|
||||||
|
|
||||||
```
|
|
||||||
basecli send --from=alice --amount=10000mycoin --to=cosmos18se8tz6kwwfga6k2yjsu7n64e9z52nen29rhzz
|
|
||||||
--sequence=0 --chain-id=test-chain-AE4XQo
|
|
||||||
```
|
|
||||||
|
|
||||||
Flag Descriptions:
|
|
||||||
- `from` is the name you gave your key
|
|
||||||
- `mycoin` is the name of the token for this basecoin demo, initialized in the genesis.json file
|
|
||||||
- `sequence` is a tally of how many transactions have been made by this account. Since this is the first tx on this account, it is 0
|
|
||||||
- `chain-id` is the unique ID that helps tendermint identify which network to connect to. You can find it in the terminal output from the gaiad daemon in the header block , or in the genesis.json file at `~/.basecoind/config/genesis.json`
|
|
||||||
|
|
||||||
Now if we check bobs account, it should have `10000 mycoin`. You can do so by running :
|
|
||||||
|
|
||||||
```
|
|
||||||
basecli account cosmos18se8tz6kwwfga6k2yjsu7n64e9z52nen29rhzz
|
|
||||||
```
|
|
||||||
|
|
||||||
Now lets send some from bob to charlie. Make sure you send less than bob has, otherwise the transaction will fail:
|
|
||||||
|
|
||||||
```
|
|
||||||
basecli send --from=bob --amount=5000mycoin --to=cosmos13wq5mklhn03ljpd4dkph5rflk5a3ssma2ag07q
|
|
||||||
--sequence=0 --chain-id=test-chain-AE4XQo
|
|
||||||
```
|
|
||||||
|
|
||||||
Note how we use the ``--from`` flag to select a different account to send from.
|
|
||||||
|
|
||||||
Lets now try to send from bob back to alice:
|
|
||||||
|
|
||||||
```
|
|
||||||
basecli send --from=bob --amount=3000mycoin --to=cosmos1khygs0qh7gz3p4m39u00mjhvgvc2dcpxhsuh5f
|
|
||||||
--sequence=1 --chain-id=test-chain-AE4XQo
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice that the sequence is now 1, since we have already recorded bobs 1st transaction as `sequence 0`. Also note the ``hash`` value in the response in the terminal - this is the hash of the transaction. We can query for the transaction with this command:
|
|
||||||
|
|
||||||
```
|
|
||||||
basecli tx <INSERT HASH HERE>
|
|
||||||
```
|
|
||||||
|
|
||||||
It will return the details of the transaction hash, such as how many coins were send and to which address, and on what block it occurred.
|
|
||||||
|
|
||||||
That is the basic implementation of basecoin!
|
|
||||||
|
|
||||||
|
|
||||||
## Reset the basecoind blockchain and basecli data
|
|
||||||
|
|
||||||
**WARNING:** Running these commands will wipe out any existing
|
|
||||||
information in both the ``~/.basecli`` and ``~/.basecoind`` directories,
|
|
||||||
including private keys. This should be no problem considering that basecoin
|
|
||||||
is just an example, but it is always good to pay extra attention when
|
|
||||||
you are removing private keys, in any scenario involving a blockchain.
|
|
||||||
|
|
||||||
To remove all the files created and refresh your environment (e.g., if
|
|
||||||
starting this tutorial again or trying something new), the following
|
|
||||||
commands are run:
|
|
||||||
|
|
||||||
```
|
|
||||||
basecoind unsafe-reset-all
|
|
||||||
rm -rf ~/.basecoind
|
|
||||||
rm -rf ~/.basecli
|
|
||||||
```
|
|
||||||
|
|
||||||
## Technical Details on how Basecoin Works
|
|
||||||
|
|
||||||
This section describes some of the more technical aspects for what is going on under the hood of Basecoin.
|
|
||||||
|
|
||||||
## Proof
|
|
||||||
|
|
||||||
Even if you don't see it in the UI, the result of every query comes with
|
|
||||||
a proof. This is a Merkle proof that the result of the query is actually
|
|
||||||
contained in the state. And the state's Merkle root is contained in a
|
|
||||||
recent block header. Behind the scenes, ``basecli`` will not only
|
|
||||||
verify that this state matches the header, but also that the header is
|
|
||||||
properly signed by the known validator set. It will even update the
|
|
||||||
validator set as needed, so long as there have not been major changes
|
|
||||||
and it is secure to do so. So, if you wonder why the query may take a
|
|
||||||
second... there is a lot of work going on in the background to make sure
|
|
||||||
even a lying full node can't trick your client.
|
|
||||||
|
|
||||||
## Accounts and Transactions
|
|
||||||
|
|
||||||
For a better understanding of how to further use the tools, it helps to
|
|
||||||
understand the underlying data structures, so lets look at accounts and transactions.
|
|
||||||
|
|
||||||
### Accounts
|
|
||||||
|
|
||||||
The Basecoin state consists entirely of a set of accounts. Each account
|
|
||||||
contains an address, a public key, a balance in many different coin denominations,
|
|
||||||
and a strictly increasing sequence number for replay protection. This
|
|
||||||
type of account was directly inspired by accounts in Ethereum, and is
|
|
||||||
unlike Bitcoin's use of Unspent Transaction Outputs (UTXOs).
|
|
||||||
|
|
||||||
```
|
|
||||||
type BaseAccount struct {
|
|
||||||
Address sdk.Address `json:"address"`
|
|
||||||
Coins sdk.Coins `json:"coins"`
|
|
||||||
PubKey crypto.PubKey `json:"public_key"`
|
|
||||||
Sequence int64 `json:"sequence"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also add more fields to accounts, and basecoin actually does so. Basecoin
|
|
||||||
adds a Name field in order to show how easily the base account structure can be
|
|
||||||
modified to suit any applications needs. It takes the `auth.BaseAccount` we see above,
|
|
||||||
and extends it with `Name`.
|
|
||||||
|
|
||||||
```
|
|
||||||
type AppAccount struct {
|
|
||||||
auth.BaseAccount
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Within accounts, coin balances are stored. Basecoin is a multi-asset cryptocurrency, so each account can have many
|
|
||||||
different kinds of tokens, which are held in an array.
|
|
||||||
|
|
||||||
```
|
|
||||||
type Coins []Coin
|
|
||||||
|
|
||||||
type Coin struct {
|
|
||||||
Denom string `json:"denom"`
|
|
||||||
Amount int64 `json:"amount"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to add more coins to a blockchain, you can do so manually in
|
|
||||||
the ``~/.basecoin/genesis.json`` before you start the blockchain for the
|
|
||||||
first time.
|
|
||||||
|
|
||||||
Accounts are serialized and stored in a Merkle tree under the key
|
|
||||||
``base/a/<address>``, where ``<address>`` is the address of the account.
|
|
||||||
Typically, the address of the account is the first 20-bytes of the ``sha256`` hash
|
|
||||||
of the public key, but other formats are acceptable as well, as defined
|
|
||||||
in the `Tendermint crypto
|
|
||||||
library <https://github.com/tendermint/tendermint/tree/master/crypto>`__. The Merkle tree
|
|
||||||
used in Basecoin is a balanced, binary search tree, which we call an
|
|
||||||
`IAVL tree <https://github.com/tendermint/iavl>`__.
|
|
||||||
|
|
||||||
### Transactions
|
|
||||||
|
|
||||||
Basecoin defines a transaction type, the `SendTx`, which allows tokens
|
|
||||||
to be sent to other accounts. The `SendTx` takes a list of inputs and
|
|
||||||
a list of outputs, and transfers all the tokens listed in the inputs
|
|
||||||
from their corresponding accounts to the accounts listed in the output.
|
|
||||||
The `SendTx` is structured as follows:
|
|
||||||
```
|
|
||||||
type SendTx struct {
|
|
||||||
Gas int64 `json:"gas"`
|
|
||||||
Fee Coin `json:"fee"`
|
|
||||||
Inputs []TxInput `json:"inputs"`
|
|
||||||
Outputs []TxOutput `json:"outputs"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TxInput struct {
|
|
||||||
Address []byte `json:"address"` // Hash of the PubKey
|
|
||||||
Coins Coins `json:"coins"` //
|
|
||||||
Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput
|
|
||||||
Signature []byte `json:"signature"` // Depends on the PubKey type and the whole Tx
|
|
||||||
PubKey crypto.PubKey `json:"pub_key"` // Is present iff Sequence == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type TxOutput struct {
|
|
||||||
Address []byte `json:"address"` // Hash of the PubKey
|
|
||||||
Coins Coins `json:"coins"` //
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Note the `SendTx` includes a field for `Gas` and `Fee`. The
|
|
||||||
`Gas` limits the total amount of computation that can be done by the
|
|
||||||
transaction, while the `Fee` refers to the total amount paid in fees.
|
|
||||||
This is slightly different from Ethereum's concept of `Gas` and
|
|
||||||
`GasPrice`, where `Fee = Gas x GasPrice`. In Basecoin, the `Gas`
|
|
||||||
and `Fee` are independent, and the `GasPrice` is implicit.
|
|
||||||
|
|
||||||
In Basecoin, the `Fee` is meant to be used by the validators to inform
|
|
||||||
the ordering of transactions, like in Bitcoin. And the `Gas` is meant
|
|
||||||
to be used by the application plugin to control its execution. There is
|
|
||||||
currently no means to pass `Fee` information to the Tendermint
|
|
||||||
validators, but it will come soon... so this version of Basecoin does
|
|
||||||
not actually fully implement fees and gas, but it still allows us
|
|
||||||
to send transactions between accounts.
|
|
||||||
|
|
||||||
Note also that the `PubKey` only needs to be sent for
|
|
||||||
`Sequence == 0`. After that, it is stored under the account in the
|
|
||||||
Merkle tree and subsequent transactions can exclude it, using only the
|
|
||||||
`Address` to refer to the sender. Ethereum does not require public
|
|
||||||
keys to be sent in transactions as it uses a different elliptic curve
|
|
||||||
scheme which enables the public key to be derived from the signature
|
|
||||||
itself.
|
|
||||||
|
|
||||||
Finally, note that the use of multiple inputs and multiple outputs
|
|
||||||
allows us to send many different types of tokens between many different
|
|
||||||
accounts at once in an atomic transaction. Thus, the `SendTx` can
|
|
||||||
serve as a basic unit of decentralized exchange. When using multiple
|
|
||||||
inputs and outputs, you must make sure that the sum of coins of the
|
|
||||||
inputs equals the sum of coins of the outputs (no creating money), and
|
|
||||||
that all accounts that provide inputs have signed the transaction.
|
|
|
@ -1,199 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
|
||||||
|
|
||||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/basecoin/types"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
appName = "BasecoinApp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// default home directories for expected binaries
|
|
||||||
var (
|
|
||||||
DefaultCLIHome = os.ExpandEnv("$HOME/.basecli")
|
|
||||||
DefaultNodeHome = os.ExpandEnv("$HOME/.basecoind")
|
|
||||||
)
|
|
||||||
|
|
||||||
// BasecoinApp implements an extended ABCI application. It contains a BaseApp,
|
|
||||||
// a codec for serialization, KVStore keys for multistore state management, and
|
|
||||||
// various mappers and keepers to manage getting, setting, and serializing the
|
|
||||||
// integral app types.
|
|
||||||
type BasecoinApp struct {
|
|
||||||
*bam.BaseApp
|
|
||||||
cdc *codec.Codec
|
|
||||||
|
|
||||||
// keys to access the multistore
|
|
||||||
keyMain *sdk.KVStoreKey
|
|
||||||
keyAccount *sdk.KVStoreKey
|
|
||||||
keyIBC *sdk.KVStoreKey
|
|
||||||
keyParams *sdk.KVStoreKey
|
|
||||||
tkeyParams *sdk.TransientStoreKey
|
|
||||||
|
|
||||||
// manage getting and setting accounts
|
|
||||||
accountKeeper auth.AccountKeeper
|
|
||||||
feeCollectionKeeper auth.FeeCollectionKeeper
|
|
||||||
bankKeeper bank.Keeper
|
|
||||||
ibcMapper ibc.Mapper
|
|
||||||
paramsKeeper params.Keeper
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBasecoinApp returns a reference to a new BasecoinApp given a logger and
|
|
||||||
// database. Internally, a codec is created along with all the necessary keys.
|
|
||||||
// In addition, all necessary mappers and keepers are created, routes
|
|
||||||
// registered, and finally the stores being mounted along with any necessary
|
|
||||||
// chain initialization.
|
|
||||||
func NewBasecoinApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseApp)) *BasecoinApp {
|
|
||||||
// create and register app-level codec for TXs and accounts
|
|
||||||
cdc := MakeCodec()
|
|
||||||
|
|
||||||
// create your application type
|
|
||||||
var app = &BasecoinApp{
|
|
||||||
cdc: cdc,
|
|
||||||
BaseApp: bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...),
|
|
||||||
keyMain: sdk.NewKVStoreKey(bam.MainStoreKey),
|
|
||||||
keyAccount: sdk.NewKVStoreKey(auth.StoreKey),
|
|
||||||
keyIBC: sdk.NewKVStoreKey("ibc"),
|
|
||||||
keyParams: sdk.NewKVStoreKey("params"),
|
|
||||||
tkeyParams: sdk.NewTransientStoreKey("transient_params"),
|
|
||||||
}
|
|
||||||
|
|
||||||
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams)
|
|
||||||
|
|
||||||
// define and attach the mappers and keepers
|
|
||||||
app.accountKeeper = auth.NewAccountKeeper(
|
|
||||||
cdc,
|
|
||||||
app.keyAccount, // target store
|
|
||||||
app.paramsKeeper.Subspace(auth.DefaultParamspace),
|
|
||||||
func() auth.Account {
|
|
||||||
return &types.AppAccount{}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper)
|
|
||||||
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, ibc.DefaultCodespace)
|
|
||||||
|
|
||||||
// register message routes
|
|
||||||
app.Router().
|
|
||||||
AddRoute("bank", bank.NewHandler(app.bankKeeper)).
|
|
||||||
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.bankKeeper))
|
|
||||||
|
|
||||||
// perform initialization logic
|
|
||||||
app.SetInitChainer(app.initChainer)
|
|
||||||
app.SetBeginBlocker(app.BeginBlocker)
|
|
||||||
app.SetEndBlocker(app.EndBlocker)
|
|
||||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper))
|
|
||||||
|
|
||||||
// mount the multistore and load the latest state
|
|
||||||
app.MountStores(app.keyMain, app.keyAccount, app.keyIBC)
|
|
||||||
err := app.LoadLatestVersion(app.keyMain)
|
|
||||||
if err != nil {
|
|
||||||
cmn.Exit(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Seal()
|
|
||||||
|
|
||||||
return app
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeCodec creates a new codec codec and registers all the necessary types
|
|
||||||
// with the codec.
|
|
||||||
func MakeCodec() *codec.Codec {
|
|
||||||
cdc := codec.New()
|
|
||||||
|
|
||||||
codec.RegisterCrypto(cdc)
|
|
||||||
sdk.RegisterCodec(cdc)
|
|
||||||
bank.RegisterCodec(cdc)
|
|
||||||
ibc.RegisterCodec(cdc)
|
|
||||||
auth.RegisterCodec(cdc)
|
|
||||||
|
|
||||||
// register custom type
|
|
||||||
cdc.RegisterConcrete(&types.AppAccount{}, "basecoin/Account", nil)
|
|
||||||
|
|
||||||
cdc.Seal()
|
|
||||||
|
|
||||||
return cdc
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeginBlocker reflects logic to run before any TXs application are processed
|
|
||||||
// by the application.
|
|
||||||
func (app *BasecoinApp) BeginBlocker(_ sdk.Context, _ abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
|
||||||
return abci.ResponseBeginBlock{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EndBlocker reflects logic to run after all TXs are processed by the
|
|
||||||
// application.
|
|
||||||
func (app *BasecoinApp) EndBlocker(_ sdk.Context, _ abci.RequestEndBlock) abci.ResponseEndBlock {
|
|
||||||
return abci.ResponseEndBlock{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// initChainer implements the custom application logic that the BaseApp will
|
|
||||||
// invoke upon initialization. In this case, it will take the application's
|
|
||||||
// state provided by 'req' and attempt to deserialize said state. The state
|
|
||||||
// should contain all the genesis accounts. These accounts will be added to the
|
|
||||||
// application's account mapper.
|
|
||||||
func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
|
||||||
stateJSON := req.AppStateBytes
|
|
||||||
|
|
||||||
genesisState := new(types.GenesisState)
|
|
||||||
err := app.cdc.UnmarshalJSON(stateJSON, genesisState)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: https://github.com/cosmos/cosmos-sdk/issues/468
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, gacc := range genesisState.Accounts {
|
|
||||||
acc, err := gacc.ToAppAccount()
|
|
||||||
if err != nil {
|
|
||||||
// TODO: https://github.com/cosmos/cosmos-sdk/issues/468
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
acc.AccountNumber = app.accountKeeper.GetNextAccountNumber(ctx)
|
|
||||||
app.accountKeeper.SetAccount(ctx, acc)
|
|
||||||
}
|
|
||||||
|
|
||||||
return abci.ResponseInitChain{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportAppStateAndValidators implements custom application logic that exposes
|
|
||||||
// various parts of the application's state and set of validators. An error is
|
|
||||||
// returned if any step getting the state or set of validators fails.
|
|
||||||
func (app *BasecoinApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
|
|
||||||
ctx := app.NewContext(true, abci.Header{})
|
|
||||||
accounts := []*types.GenesisAccount{}
|
|
||||||
|
|
||||||
appendAccountsFn := func(acc auth.Account) bool {
|
|
||||||
account := &types.GenesisAccount{
|
|
||||||
Address: acc.GetAddress(),
|
|
||||||
Coins: acc.GetCoins(),
|
|
||||||
}
|
|
||||||
|
|
||||||
accounts = append(accounts, account)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
app.accountKeeper.IterateAccounts(ctx, appendAccountsFn)
|
|
||||||
|
|
||||||
genState := types.GenesisState{Accounts: accounts}
|
|
||||||
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return appState, validators, err
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/basecoin/types"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setGenesis(baseApp *BasecoinApp, accounts ...*types.AppAccount) (types.GenesisState, error) {
|
|
||||||
genAccts := make([]*types.GenesisAccount, len(accounts))
|
|
||||||
for i, appAct := range accounts {
|
|
||||||
genAccts[i] = types.NewGenesisAccount(appAct)
|
|
||||||
}
|
|
||||||
|
|
||||||
genesisState := types.GenesisState{Accounts: genAccts}
|
|
||||||
stateBytes, err := codec.MarshalJSONIndent(baseApp.cdc, genesisState)
|
|
||||||
if err != nil {
|
|
||||||
return types.GenesisState{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize and commit the chain
|
|
||||||
baseApp.InitChain(abci.RequestInitChain{
|
|
||||||
Validators: []abci.ValidatorUpdate{}, AppStateBytes: stateBytes,
|
|
||||||
})
|
|
||||||
baseApp.Commit()
|
|
||||||
|
|
||||||
return genesisState, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenesis(t *testing.T) {
|
|
||||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
|
||||||
db := dbm.NewMemDB()
|
|
||||||
baseApp := NewBasecoinApp(logger, db)
|
|
||||||
|
|
||||||
// construct a pubkey and an address for the test account
|
|
||||||
pubkey := ed25519.GenPrivKey().PubKey()
|
|
||||||
addr := sdk.AccAddress(pubkey.Address())
|
|
||||||
|
|
||||||
// construct some test coins
|
|
||||||
coins, err := sdk.ParseCoins("77foocoin,99barcoin")
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
// create an auth.BaseAccount for the given test account and set it's coins
|
|
||||||
baseAcct := auth.NewBaseAccountWithAddress(addr)
|
|
||||||
err = baseAcct.SetCoins(coins)
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
// create a new test AppAccount with the given auth.BaseAccount
|
|
||||||
appAcct := types.NewAppAccount("foobar", baseAcct)
|
|
||||||
genState, err := setGenesis(baseApp, appAcct)
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
// create a context for the BaseApp
|
|
||||||
ctx := baseApp.BaseApp.NewContext(true, abci.Header{})
|
|
||||||
res := baseApp.accountKeeper.GetAccount(ctx, baseAcct.Address)
|
|
||||||
require.Equal(t, appAcct, res)
|
|
||||||
|
|
||||||
// reload app and ensure the account is still there
|
|
||||||
baseApp = NewBasecoinApp(logger, db)
|
|
||||||
|
|
||||||
stateBytes, err := codec.MarshalJSONIndent(baseApp.cdc, genState)
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
// initialize the chain with the expected genesis state
|
|
||||||
baseApp.InitChain(abci.RequestInitChain{
|
|
||||||
Validators: []abci.ValidatorUpdate{}, AppStateBytes: stateBytes,
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx = baseApp.BaseApp.NewContext(true, abci.Header{})
|
|
||||||
res = baseApp.accountKeeper.GetAccount(ctx, baseAcct.Address)
|
|
||||||
require.Equal(t, appAcct, res)
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package clitest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
|
||||||
"github.com/cosmos/cosmos-sdk/tests"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
basecoindHome = ""
|
|
||||||
basecliHome = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
basecoindHome, basecliHome = getTestingHomeDirs()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInitStartSequence(t *testing.T) {
|
|
||||||
os.RemoveAll(basecoindHome)
|
|
||||||
servAddr, port, err := server.FreeTCPAddr()
|
|
||||||
require.NoError(t, err)
|
|
||||||
executeInit(t)
|
|
||||||
executeStart(t, servAddr, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
func executeInit(t *testing.T) {
|
|
||||||
var (
|
|
||||||
chainID string
|
|
||||||
initRes map[string]json.RawMessage
|
|
||||||
)
|
|
||||||
_, stderr := tests.ExecuteT(t, fmt.Sprintf("basecoind --home=%s --home-client=%s init --name=test", basecoindHome, basecliHome), app.DefaultKeyPass)
|
|
||||||
err := json.Unmarshal([]byte(stderr), &initRes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = json.Unmarshal(initRes["chain_id"], &chainID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func executeStart(t *testing.T, servAddr, port string) {
|
|
||||||
proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("basecoind start --home=%s --rpc.laddr=%v", basecoindHome, servAddr))
|
|
||||||
defer proc.Stop(false)
|
|
||||||
tests.WaitForTMStart(port)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTestingHomeDirs() (string, string) {
|
|
||||||
tmpDir := os.TempDir()
|
|
||||||
basecoindHome := fmt.Sprintf("%s%s.test_basecoind", tmpDir, string(os.PathSeparator))
|
|
||||||
basecliHome := fmt.Sprintf("%s%s.test_basecli", tmpDir, string(os.PathSeparator))
|
|
||||||
return basecoindHome, basecliHome
|
|
||||||
}
|
|
|
@ -1,123 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/lcd"
|
|
||||||
_ "github.com/cosmos/cosmos-sdk/client/lcd/statik"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/basecoin/app"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/tendermint/tendermint/libs/cli"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/version"
|
|
||||||
at "github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
|
||||||
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
|
||||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
|
|
||||||
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
|
|
||||||
ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli"
|
|
||||||
sl "github.com/cosmos/cosmos-sdk/x/slashing"
|
|
||||||
slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
|
|
||||||
slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest"
|
|
||||||
st "github.com/cosmos/cosmos-sdk/x/staking"
|
|
||||||
stakingcmd "github.com/cosmos/cosmos-sdk/x/staking/client/cli"
|
|
||||||
staking "github.com/cosmos/cosmos-sdk/x/staking/client/rest"
|
|
||||||
)
|
|
||||||
|
|
||||||
// rootCmd is the entry point for this binary
|
|
||||||
var (
|
|
||||||
rootCmd = &cobra.Command{
|
|
||||||
Use: "basecli",
|
|
||||||
Short: "Basecoin light-client",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// disable sorting
|
|
||||||
cobra.EnableCommandSorting = false
|
|
||||||
|
|
||||||
// get the codec
|
|
||||||
cdc := app.MakeCodec()
|
|
||||||
|
|
||||||
// Setup certain SDK config
|
|
||||||
config := sdk.GetConfig()
|
|
||||||
config.SetBech32PrefixForAccount("baseacc", "basepub")
|
|
||||||
config.SetBech32PrefixForValidator("baseval", "basevalpub")
|
|
||||||
config.SetBech32PrefixForConsensusNode("basecons", "baseconspub")
|
|
||||||
config.Seal()
|
|
||||||
|
|
||||||
// TODO: Setup keybase, viper object, etc. to be passed into
|
|
||||||
// the below functions and eliminate global vars, like we do
|
|
||||||
// with the cdc.
|
|
||||||
|
|
||||||
// add standard rpc, and tx commands
|
|
||||||
rootCmd.AddCommand(
|
|
||||||
rpc.StatusCommand(),
|
|
||||||
client.LineBreak,
|
|
||||||
tx.SearchTxCmd(cdc),
|
|
||||||
tx.QueryTxCmd(cdc),
|
|
||||||
client.LineBreak,
|
|
||||||
)
|
|
||||||
|
|
||||||
// add query/post commands (custom to binary)
|
|
||||||
rootCmd.AddCommand(
|
|
||||||
stakingcmd.GetCmdQueryValidator(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryValidators(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryValidatorUnbondingDelegations(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryValidatorRedelegations(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryDelegation(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryDelegations(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryPool(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryParams(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryUnbondingDelegation(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryUnbondingDelegations(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryRedelegation(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryRedelegations(st.StoreKey, cdc),
|
|
||||||
slashingcmd.GetCmdQuerySigningInfo(sl.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdQueryValidatorDelegations(st.StoreKey, cdc),
|
|
||||||
authcmd.GetAccountCmd(at.StoreKey, cdc),
|
|
||||||
)
|
|
||||||
|
|
||||||
rootCmd.AddCommand(
|
|
||||||
bankcmd.SendTxCmd(cdc),
|
|
||||||
ibccmd.IBCTransferCmd(cdc),
|
|
||||||
ibccmd.IBCRelayCmd(cdc),
|
|
||||||
stakingcmd.GetCmdCreateValidator(cdc),
|
|
||||||
stakingcmd.GetCmdEditValidator(cdc),
|
|
||||||
stakingcmd.GetCmdDelegate(cdc),
|
|
||||||
stakingcmd.GetCmdUnbond(st.StoreKey, cdc),
|
|
||||||
stakingcmd.GetCmdRedelegate(st.StoreKey, cdc),
|
|
||||||
slashingcmd.GetCmdUnjail(cdc),
|
|
||||||
)
|
|
||||||
|
|
||||||
// add proxy, version and key info
|
|
||||||
rootCmd.AddCommand(
|
|
||||||
client.LineBreak,
|
|
||||||
lcd.ServeCommand(cdc, registerRoutes),
|
|
||||||
keys.Commands(),
|
|
||||||
client.LineBreak,
|
|
||||||
version.VersionCmd,
|
|
||||||
)
|
|
||||||
|
|
||||||
// prepare and add flags
|
|
||||||
executor := cli.PrepareMainCmd(rootCmd, "BC", app.DefaultCLIHome)
|
|
||||||
err := executor.Execute()
|
|
||||||
if err != nil {
|
|
||||||
// Note: Handle with #870
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerRoutes(rs *lcd.RestServer) {
|
|
||||||
keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent)
|
|
||||||
rpc.RegisterRoutes(rs.CliCtx, rs.Mux)
|
|
||||||
tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc)
|
|
||||||
auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, at.StoreKey)
|
|
||||||
bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase)
|
|
||||||
staking.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase)
|
|
||||||
slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase)
|
|
||||||
}
|
|
|
@ -1,148 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/p2p"
|
|
||||||
"github.com/tendermint/tendermint/privval"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
||||||
gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tendermint/libs/cli"
|
|
||||||
"github.com/tendermint/tendermint/libs/common"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/basecoin/app"
|
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
flagClientHome = "home-client"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
cdc := app.MakeCodec()
|
|
||||||
ctx := server.NewDefaultContext()
|
|
||||||
|
|
||||||
rootCmd := &cobra.Command{
|
|
||||||
Use: "basecoind",
|
|
||||||
Short: "Basecoin Daemon (server)",
|
|
||||||
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
|
|
||||||
}
|
|
||||||
|
|
||||||
rootCmd.AddCommand(InitCmd(ctx, cdc))
|
|
||||||
|
|
||||||
server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators)
|
|
||||||
|
|
||||||
// prepare and add flags
|
|
||||||
rootDir := os.ExpandEnv("$HOME/.basecoind")
|
|
||||||
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
|
|
||||||
|
|
||||||
// initialise the Bech32 prefixes
|
|
||||||
initSDKConfig()
|
|
||||||
|
|
||||||
err := executor.Execute()
|
|
||||||
if err != nil {
|
|
||||||
// Note: Handle with #870
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initSDKConfig() {
|
|
||||||
config := sdk.GetConfig()
|
|
||||||
config.SetBech32PrefixForAccount("baseacc", "basepub")
|
|
||||||
config.SetBech32PrefixForValidator("baseval", "basevalpub")
|
|
||||||
config.SetBech32PrefixForConsensusNode("basecons", "baseconspub")
|
|
||||||
config.Seal()
|
|
||||||
}
|
|
||||||
|
|
||||||
// get cmd to initialize all files for tendermint and application
|
|
||||||
// nolint: errcheck
|
|
||||||
func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "init",
|
|
||||||
Short: "Initialize genesis config, priv-validator file, and p2p-node file",
|
|
||||||
Args: cobra.NoArgs,
|
|
||||||
RunE: func(_ *cobra.Command, _ []string) error {
|
|
||||||
|
|
||||||
config := ctx.Config
|
|
||||||
config.SetRoot(viper.GetString(cli.HomeFlag))
|
|
||||||
chainID := viper.GetString(client.FlagChainID)
|
|
||||||
if chainID == "" {
|
|
||||||
chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6))
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nodeID := string(nodeKey.ID())
|
|
||||||
|
|
||||||
pk := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(),
|
|
||||||
config.PrivValidatorStateFile()).GetPubKey()
|
|
||||||
genTx, appMessage, validator, err := server.SimpleAppGenTx(cdc, pk)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
appState, err := server.SimpleAppGenState(
|
|
||||||
cdc, tmtypes.GenesisDoc{}, []json.RawMessage{genTx})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
appStateJSON, err := cdc.MarshalJSON(appState)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
toPrint := struct {
|
|
||||||
ChainID string `json:"chain_id"`
|
|
||||||
NodeID string `json:"node_id"`
|
|
||||||
AppMessage json.RawMessage `json:"app_message"`
|
|
||||||
}{
|
|
||||||
chainID,
|
|
||||||
nodeID,
|
|
||||||
appMessage,
|
|
||||||
}
|
|
||||||
out, err := codec.MarshalJSONIndent(cdc, toPrint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stderr, "%s\n", string(out))
|
|
||||||
return gaiaInit.ExportGenesisFile(config.GenesisFile(), chainID,
|
|
||||||
[]tmtypes.GenesisValidator{validator}, appStateJSON)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
|
|
||||||
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
|
|
||||||
cmd.Flags().String(client.FlagChainID, "",
|
|
||||||
"genesis file chain-id, if left blank will be randomly created")
|
|
||||||
cmd.Flags().String(client.FlagName, "", "validator's moniker")
|
|
||||||
cmd.MarkFlagRequired(client.FlagName)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer) abci.Application {
|
|
||||||
return app.NewBasecoinApp(logger, db, baseapp.SetPruning(store.NewPruningOptions(viper.GetString("pruning"))))
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, storeTracer io.Writer, _ int64, _ bool) (
|
|
||||||
json.RawMessage, []tmtypes.GenesisValidator, error) {
|
|
||||||
bapp := app.NewBasecoinApp(logger, db)
|
|
||||||
return bapp.ExportAppStateAndValidators()
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ auth.Account = (*AppAccount)(nil)
|
|
||||||
|
|
||||||
// AppAccount is a custom extension for this application. It is an example of
|
|
||||||
// extending auth.BaseAccount with custom fields. It is compatible with the
|
|
||||||
// stock auth.AccountKeeper, since auth.AccountKeeper uses the flexible go-amino
|
|
||||||
// library.
|
|
||||||
type AppAccount struct {
|
|
||||||
auth.BaseAccount
|
|
||||||
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint
|
|
||||||
func (acc AppAccount) GetName() string { return acc.Name }
|
|
||||||
func (acc *AppAccount) SetName(name string) { acc.Name = name }
|
|
||||||
|
|
||||||
// NewAppAccount returns a reference to a new AppAccount given a name and an
|
|
||||||
// auth.BaseAccount.
|
|
||||||
func NewAppAccount(name string, baseAcct auth.BaseAccount) *AppAccount {
|
|
||||||
return &AppAccount{BaseAccount: baseAcct, Name: name}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAccountDecoder returns the AccountDecoder function for the custom
|
|
||||||
// AppAccount.
|
|
||||||
func GetAccountDecoder(cdc *codec.Codec) auth.AccountDecoder {
|
|
||||||
return func(accBytes []byte) (auth.Account, error) {
|
|
||||||
if len(accBytes) == 0 {
|
|
||||||
return nil, sdk.ErrTxDecode("accBytes are empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
acct := new(AppAccount)
|
|
||||||
err := cdc.UnmarshalBinaryBare(accBytes, &acct)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return acct, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenesisState reflects the genesis state of the application.
|
|
||||||
type GenesisState struct {
|
|
||||||
Accounts []*GenesisAccount `json:"accounts"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenesisAccount reflects a genesis account the application expects in it's
|
|
||||||
// genesis state.
|
|
||||||
type GenesisAccount struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Address sdk.AccAddress `json:"address"`
|
|
||||||
Coins sdk.Coins `json:"coins"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGenesisAccount returns a reference to a new GenesisAccount given an
|
|
||||||
// AppAccount.
|
|
||||||
func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
|
|
||||||
return &GenesisAccount{
|
|
||||||
Name: aa.Name,
|
|
||||||
Address: aa.Address,
|
|
||||||
Coins: aa.Coins.Sort(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToAppAccount converts a GenesisAccount to an AppAccount.
|
|
||||||
func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) {
|
|
||||||
return &AppAccount{
|
|
||||||
Name: ga.Name,
|
|
||||||
BaseAccount: auth.BaseAccount{
|
|
||||||
Address: ga.Address,
|
|
||||||
Coins: ga.Coins.Sort(),
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,207 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
|
||||||
|
|
||||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/params"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/pow"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/simplestaking"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/sketchy"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
appName = "DemocoinApp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// default home directories for expected binaries
|
|
||||||
var (
|
|
||||||
DefaultCLIHome = os.ExpandEnv("$HOME/.democli")
|
|
||||||
DefaultNodeHome = os.ExpandEnv("$HOME/.democoind")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Extended ABCI application
|
|
||||||
type DemocoinApp struct {
|
|
||||||
*bam.BaseApp
|
|
||||||
cdc *codec.Codec
|
|
||||||
|
|
||||||
// keys to access the substores
|
|
||||||
capKeyMainStore *sdk.KVStoreKey
|
|
||||||
capKeyAccountStore *sdk.KVStoreKey
|
|
||||||
capKeyPowStore *sdk.KVStoreKey
|
|
||||||
capKeyIBCStore *sdk.KVStoreKey
|
|
||||||
capKeyStakingStore *sdk.KVStoreKey
|
|
||||||
keyParams *sdk.KVStoreKey
|
|
||||||
tkeyParams *sdk.TransientStoreKey
|
|
||||||
|
|
||||||
// keepers
|
|
||||||
paramsKeeper params.Keeper
|
|
||||||
feeCollectionKeeper auth.FeeCollectionKeeper
|
|
||||||
bankKeeper bank.Keeper
|
|
||||||
coolKeeper cool.Keeper
|
|
||||||
powKeeper pow.Keeper
|
|
||||||
ibcMapper ibc.Mapper
|
|
||||||
stakingKeeper simplestaking.Keeper
|
|
||||||
|
|
||||||
// Manage getting and setting accounts
|
|
||||||
accountKeeper auth.AccountKeeper
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
|
|
||||||
|
|
||||||
// Create app-level codec for txs and accounts.
|
|
||||||
var cdc = MakeCodec()
|
|
||||||
|
|
||||||
// Create your application object.
|
|
||||||
var app = &DemocoinApp{
|
|
||||||
BaseApp: bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc)),
|
|
||||||
cdc: cdc,
|
|
||||||
capKeyMainStore: sdk.NewKVStoreKey(bam.MainStoreKey),
|
|
||||||
capKeyAccountStore: sdk.NewKVStoreKey(auth.StoreKey),
|
|
||||||
capKeyPowStore: sdk.NewKVStoreKey("pow"),
|
|
||||||
capKeyIBCStore: sdk.NewKVStoreKey("ibc"),
|
|
||||||
capKeyStakingStore: sdk.NewKVStoreKey(staking.StoreKey),
|
|
||||||
keyParams: sdk.NewKVStoreKey("params"),
|
|
||||||
tkeyParams: sdk.NewTransientStoreKey("transient_params"),
|
|
||||||
}
|
|
||||||
|
|
||||||
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams)
|
|
||||||
|
|
||||||
// Define the accountKeeper.
|
|
||||||
app.accountKeeper = auth.NewAccountKeeper(
|
|
||||||
cdc,
|
|
||||||
app.capKeyAccountStore,
|
|
||||||
app.paramsKeeper.Subspace(auth.DefaultParamspace),
|
|
||||||
types.ProtoAppAccount,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Add handlers.
|
|
||||||
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper)
|
|
||||||
app.coolKeeper = cool.NewKeeper(app.capKeyMainStore, app.bankKeeper, cool.DefaultCodespace)
|
|
||||||
app.powKeeper = pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), app.bankKeeper, pow.DefaultCodespace)
|
|
||||||
app.ibcMapper = ibc.NewMapper(app.cdc, app.capKeyIBCStore, ibc.DefaultCodespace)
|
|
||||||
app.stakingKeeper = simplestaking.NewKeeper(app.capKeyStakingStore, app.bankKeeper, simplestaking.DefaultCodespace)
|
|
||||||
app.Router().
|
|
||||||
AddRoute("bank", bank.NewHandler(app.bankKeeper)).
|
|
||||||
AddRoute("cool", cool.NewHandler(app.coolKeeper)).
|
|
||||||
AddRoute("pow", app.powKeeper.Handler).
|
|
||||||
AddRoute("sketchy", sketchy.NewHandler()).
|
|
||||||
AddRoute("ibc", ibc.NewHandler(app.ibcMapper, app.bankKeeper)).
|
|
||||||
AddRoute("simplestaking", simplestaking.NewHandler(app.stakingKeeper))
|
|
||||||
|
|
||||||
// Initialize BaseApp.
|
|
||||||
app.SetInitChainer(app.initChainerFn(app.coolKeeper, app.powKeeper))
|
|
||||||
app.MountStores(app.capKeyMainStore, app.capKeyAccountStore, app.capKeyPowStore, app.capKeyIBCStore, app.capKeyStakingStore)
|
|
||||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper))
|
|
||||||
err := app.LoadLatestVersion(app.capKeyMainStore)
|
|
||||||
if err != nil {
|
|
||||||
cmn.Exit(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Seal()
|
|
||||||
|
|
||||||
return app
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom tx codec
|
|
||||||
func MakeCodec() *codec.Codec {
|
|
||||||
var cdc = codec.New()
|
|
||||||
codec.RegisterCrypto(cdc) // Register crypto.
|
|
||||||
sdk.RegisterCodec(cdc) // Register Msgs
|
|
||||||
cool.RegisterCodec(cdc)
|
|
||||||
pow.RegisterCodec(cdc)
|
|
||||||
bank.RegisterCodec(cdc)
|
|
||||||
ibc.RegisterCodec(cdc)
|
|
||||||
simplestaking.RegisterCodec(cdc)
|
|
||||||
|
|
||||||
// Register AppAccount
|
|
||||||
cdc.RegisterInterface((*auth.Account)(nil), nil)
|
|
||||||
cdc.RegisterConcrete(&types.AppAccount{}, "democoin/Account", nil)
|
|
||||||
|
|
||||||
cdc.Seal()
|
|
||||||
|
|
||||||
return cdc
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom logic for democoin initialization
|
|
||||||
// nolint: unparam
|
|
||||||
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 := app.cdc.UnmarshalJSON(stateJSON, genesisState)
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
|
||||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, gacc := range genesisState.Accounts {
|
|
||||||
acc, err := gacc.ToAppAccount()
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
|
||||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
|
||||||
}
|
|
||||||
app.accountKeeper.SetAccount(ctx, acc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Application specific genesis handling
|
|
||||||
err = cool.InitGenesis(ctx, app.coolKeeper, genesisState.CoolGenesis)
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
|
||||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pow.InitGenesis(ctx, app.powKeeper, genesisState.POWGenesis)
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
|
||||||
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
return abci.ResponseInitChain{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom logic for state export
|
|
||||||
func (app *DemocoinApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
|
|
||||||
ctx := app.NewContext(true, abci.Header{})
|
|
||||||
|
|
||||||
// iterate to get the accounts
|
|
||||||
accounts := []*types.GenesisAccount{}
|
|
||||||
appendAccount := func(acc auth.Account) (stop bool) {
|
|
||||||
account := &types.GenesisAccount{
|
|
||||||
Address: acc.GetAddress(),
|
|
||||||
Coins: acc.GetCoins(),
|
|
||||||
}
|
|
||||||
accounts = append(accounts, account)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
app.accountKeeper.IterateAccounts(ctx, appendAccount)
|
|
||||||
|
|
||||||
genState := types.GenesisState{
|
|
||||||
Accounts: accounts,
|
|
||||||
POWGenesis: pow.ExportGenesis(ctx, app.powKeeper),
|
|
||||||
CoolGenesis: cool.ExportGenesis(ctx, app.coolKeeper),
|
|
||||||
}
|
|
||||||
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return appState, validators, nil
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setGenesis(bapp *DemocoinApp, trend string, accs ...auth.BaseAccount) error {
|
|
||||||
genaccs := make([]*types.GenesisAccount, len(accs))
|
|
||||||
for i, acc := range accs {
|
|
||||||
genaccs[i] = types.NewGenesisAccount(&types.AppAccount{acc, "foobart"})
|
|
||||||
}
|
|
||||||
|
|
||||||
genesisState := types.GenesisState{
|
|
||||||
Accounts: genaccs,
|
|
||||||
CoolGenesis: cool.Genesis{trend},
|
|
||||||
}
|
|
||||||
|
|
||||||
stateBytes, err := codec.MarshalJSONIndent(bapp.cdc, genesisState)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the chain
|
|
||||||
vals := []abci.ValidatorUpdate{}
|
|
||||||
bapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
|
|
||||||
bapp.Commit()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenesis(t *testing.T) {
|
|
||||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
|
||||||
db := dbm.NewMemDB()
|
|
||||||
bapp := NewDemocoinApp(logger, db)
|
|
||||||
|
|
||||||
// Construct some genesis bytes to reflect democoin/types/AppAccount
|
|
||||||
pk := ed25519.GenPrivKey().PubKey()
|
|
||||||
addr := sdk.AccAddress(pk.Address())
|
|
||||||
coins, err := sdk.ParseCoins("77foocoin,99barcoin")
|
|
||||||
require.Nil(t, err)
|
|
||||||
baseAcc := auth.BaseAccount{
|
|
||||||
Address: addr,
|
|
||||||
Coins: coins,
|
|
||||||
}
|
|
||||||
acc := &types.AppAccount{baseAcc, "foobart"}
|
|
||||||
|
|
||||||
err = setGenesis(bapp, "ice-cold", baseAcc)
|
|
||||||
require.Nil(t, err)
|
|
||||||
// A checkTx context
|
|
||||||
ctx := bapp.BaseApp.NewContext(true, abci.Header{})
|
|
||||||
res1 := bapp.accountKeeper.GetAccount(ctx, baseAcc.Address)
|
|
||||||
require.Equal(t, acc, res1)
|
|
||||||
|
|
||||||
// reload app and ensure the account is still there
|
|
||||||
bapp = NewDemocoinApp(logger, db)
|
|
||||||
bapp.InitChain(abci.RequestInitChain{AppStateBytes: []byte("{}")})
|
|
||||||
ctx = bapp.BaseApp.NewContext(true, abci.Header{})
|
|
||||||
res1 = bapp.accountKeeper.GetAccount(ctx, baseAcc.Address)
|
|
||||||
require.Equal(t, acc, res1)
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package clitest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
|
||||||
"github.com/cosmos/cosmos-sdk/tests"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
democoindHome = ""
|
|
||||||
democliHome = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
democoindHome, democliHome = getTestingHomeDirs()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInitStartSequence(t *testing.T) {
|
|
||||||
os.RemoveAll(democoindHome)
|
|
||||||
servAddr, port, err := server.FreeTCPAddr()
|
|
||||||
require.NoError(t, err)
|
|
||||||
executeInit(t)
|
|
||||||
executeStart(t, servAddr, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
func executeInit(t *testing.T) {
|
|
||||||
var (
|
|
||||||
chainID string
|
|
||||||
initRes map[string]json.RawMessage
|
|
||||||
)
|
|
||||||
_, stderr := tests.ExecuteT(t, fmt.Sprintf("democoind --home=%s --home-client=%s init --name=test", democoindHome, democliHome), app.DefaultKeyPass)
|
|
||||||
err := json.Unmarshal([]byte(stderr), &initRes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = json.Unmarshal(initRes["chain_id"], &chainID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func executeStart(t *testing.T, servAddr, port string) {
|
|
||||||
proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("democoind start --home=%s --rpc.laddr=%v", democoindHome, servAddr))
|
|
||||||
defer proc.Stop(false)
|
|
||||||
tests.WaitForTMStart(port)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTestingHomeDirs() (string, string) {
|
|
||||||
tmpDir := os.TempDir()
|
|
||||||
democoindHome := fmt.Sprintf("%s%s.test_democoind", tmpDir, string(os.PathSeparator))
|
|
||||||
democliHome := fmt.Sprintf("%s%s.test_democli", tmpDir, string(os.PathSeparator))
|
|
||||||
return democoindHome, democliHome
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/libs/cli"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/lcd"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/version"
|
|
||||||
at "github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
|
||||||
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
|
||||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
|
|
||||||
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/app"
|
|
||||||
coolcmd "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool/client/cli"
|
|
||||||
powcmd "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/pow/client/cli"
|
|
||||||
simplestakingcmd "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/simplestaking/client/cli"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// rootCmd is the entry point for this binary
|
|
||||||
var (
|
|
||||||
rootCmd = &cobra.Command{
|
|
||||||
Use: "democli",
|
|
||||||
Short: "Democoin light-client",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// disable sorting
|
|
||||||
cobra.EnableCommandSorting = false
|
|
||||||
|
|
||||||
// get the codec
|
|
||||||
cdc := app.MakeCodec()
|
|
||||||
|
|
||||||
// Setup certain SDK config
|
|
||||||
config := sdk.GetConfig()
|
|
||||||
config.SetBech32PrefixForAccount("demoacc", "demopub")
|
|
||||||
config.SetBech32PrefixForValidator("demoval", "demovalpub")
|
|
||||||
config.SetBech32PrefixForConsensusNode("democons", "democonspub")
|
|
||||||
config.Seal()
|
|
||||||
|
|
||||||
// TODO: setup keybase, viper object, etc. to be passed into
|
|
||||||
// the below functions and eliminate global vars, like we do
|
|
||||||
// with the cdc
|
|
||||||
|
|
||||||
// add standard rpc, and tx commands
|
|
||||||
|
|
||||||
rootCmd.AddCommand(
|
|
||||||
rpc.StatusCommand(),
|
|
||||||
client.LineBreak,
|
|
||||||
tx.SearchTxCmd(cdc),
|
|
||||||
tx.QueryTxCmd(cdc),
|
|
||||||
client.LineBreak,
|
|
||||||
)
|
|
||||||
|
|
||||||
// add query/post commands (custom to binary)
|
|
||||||
// start with commands common to basecoin
|
|
||||||
rootCmd.AddCommand(
|
|
||||||
authcmd.GetAccountCmd(at.StoreKey, cdc),
|
|
||||||
)
|
|
||||||
rootCmd.AddCommand(
|
|
||||||
bankcmd.SendTxCmd(cdc),
|
|
||||||
)
|
|
||||||
rootCmd.AddCommand(
|
|
||||||
client.PostCommands(
|
|
||||||
simplestakingcmd.BondTxCmd(cdc),
|
|
||||||
)...)
|
|
||||||
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
|
|
||||||
rootCmd.AddCommand(
|
|
||||||
client.LineBreak,
|
|
||||||
lcd.ServeCommand(cdc, registerRoutes),
|
|
||||||
keys.Commands(),
|
|
||||||
client.LineBreak,
|
|
||||||
version.VersionCmd,
|
|
||||||
)
|
|
||||||
|
|
||||||
// prepare and add flags
|
|
||||||
executor := cli.PrepareMainCmd(rootCmd, "BC", app.DefaultCLIHome)
|
|
||||||
err := executor.Execute()
|
|
||||||
if err != nil {
|
|
||||||
// handle with #870
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerRoutes(rs *lcd.RestServer) {
|
|
||||||
keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent)
|
|
||||||
rpc.RegisterRoutes(rs.CliCtx, rs.Mux)
|
|
||||||
tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc)
|
|
||||||
auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, at.StoreKey)
|
|
||||||
bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase)
|
|
||||||
}
|
|
|
@ -1,171 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"github.com/tendermint/tendermint/libs/common"
|
|
||||||
"github.com/tendermint/tendermint/p2p"
|
|
||||||
"github.com/tendermint/tendermint/privval"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tendermint/libs/cli"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
|
||||||
|
|
||||||
gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init"
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/app"
|
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
flagClientHome = "home-client"
|
|
||||||
)
|
|
||||||
|
|
||||||
// coolGenAppParams sets up the app_state and appends the cool app state
|
|
||||||
func CoolAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) (
|
|
||||||
appState json.RawMessage, err error) {
|
|
||||||
appState, err = server.SimpleAppGenState(cdc, tmtypes.GenesisDoc{}, appGenTxs)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
key := "cool"
|
|
||||||
value := json.RawMessage(`{
|
|
||||||
"trend": "ice-cold"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
appState, err = server.InsertKeyJSON(cdc, appState, key, value)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
key = "pow"
|
|
||||||
value = json.RawMessage(`{
|
|
||||||
"difficulty": "1",
|
|
||||||
"count": "0"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
appState, err = server.InsertKeyJSON(cdc, appState, key, value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// get cmd to initialize all files for tendermint and application
|
|
||||||
// nolint: errcheck
|
|
||||||
func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "init",
|
|
||||||
Short: "Initialize genesis config, priv-validator file, and p2p-node file",
|
|
||||||
Args: cobra.NoArgs,
|
|
||||||
RunE: func(_ *cobra.Command, _ []string) error {
|
|
||||||
|
|
||||||
config := ctx.Config
|
|
||||||
config.SetRoot(viper.GetString(cli.HomeFlag))
|
|
||||||
chainID := viper.GetString(client.FlagChainID)
|
|
||||||
if chainID == "" {
|
|
||||||
chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6))
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nodeID := string(nodeKey.ID())
|
|
||||||
|
|
||||||
pk := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(),
|
|
||||||
config.PrivValidatorStateFile()).GetPubKey()
|
|
||||||
genTx, appMessage, validator, err := server.SimpleAppGenTx(cdc, pk)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
appState, err := CoolAppGenState(cdc, tmtypes.GenesisDoc{},
|
|
||||||
[]json.RawMessage{genTx})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
appStateJSON, err := cdc.MarshalJSON(appState)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
toPrint := struct {
|
|
||||||
ChainID string `json:"chain_id"`
|
|
||||||
NodeID string `json:"node_id"`
|
|
||||||
AppMessage json.RawMessage `json:"app_message"`
|
|
||||||
}{
|
|
||||||
chainID,
|
|
||||||
nodeID,
|
|
||||||
appMessage,
|
|
||||||
}
|
|
||||||
out, err := codec.MarshalJSONIndent(cdc, toPrint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stderr, "%s\n", string(out))
|
|
||||||
return gaiaInit.ExportGenesisFile(config.GenesisFile(), chainID,
|
|
||||||
[]tmtypes.GenesisValidator{validator}, appStateJSON)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
|
|
||||||
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
|
|
||||||
cmd.Flags().String(client.FlagChainID, "",
|
|
||||||
"genesis file chain-id, if left blank will be randomly created")
|
|
||||||
cmd.Flags().String(client.FlagName, "", "validator's moniker")
|
|
||||||
cmd.MarkFlagRequired(client.FlagName)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func newApp(logger log.Logger, db dbm.DB, _ io.Writer) abci.Application {
|
|
||||||
return app.NewDemocoinApp(logger, db)
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer, _ int64, _ bool) (
|
|
||||||
json.RawMessage, []tmtypes.GenesisValidator, error) {
|
|
||||||
dapp := app.NewDemocoinApp(logger, db)
|
|
||||||
return dapp.ExportAppStateAndValidators()
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
cdc := app.MakeCodec()
|
|
||||||
|
|
||||||
// Setup certain SDK config
|
|
||||||
config := sdk.GetConfig()
|
|
||||||
config.SetBech32PrefixForAccount("demoacc", "demopub")
|
|
||||||
config.SetBech32PrefixForValidator("demoval", "demovalpub")
|
|
||||||
config.SetBech32PrefixForConsensusNode("democons", "democonspub")
|
|
||||||
config.Seal()
|
|
||||||
|
|
||||||
ctx := server.NewDefaultContext()
|
|
||||||
|
|
||||||
rootCmd := &cobra.Command{
|
|
||||||
Use: "democoind",
|
|
||||||
Short: "Democoin Daemon (server)",
|
|
||||||
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
|
|
||||||
}
|
|
||||||
|
|
||||||
rootCmd.AddCommand(InitCmd(ctx, cdc))
|
|
||||||
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc))
|
|
||||||
|
|
||||||
server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators)
|
|
||||||
|
|
||||||
// prepare and add flags
|
|
||||||
rootDir := os.ExpandEnv("$HOME/.democoind")
|
|
||||||
executor := cli.PrepareBaseCmd(rootCmd, "BC", rootDir)
|
|
||||||
err := executor.Execute()
|
|
||||||
if err != nil {
|
|
||||||
// handle with #870
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,168 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Validator implements sdk.Validator
|
|
||||||
type Validator struct {
|
|
||||||
Address sdk.ValAddress
|
|
||||||
Power sdk.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetStatus() sdk.BondStatus {
|
|
||||||
return sdk.Bonded
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetOperator() sdk.ValAddress {
|
|
||||||
return v.Address
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetConsPubKey() crypto.PubKey {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetConsAddr() sdk.ConsAddress {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetTokens() sdk.Int {
|
|
||||||
return sdk.ZeroInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetPower() sdk.Int {
|
|
||||||
return v.Power
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetDelegatorShares() sdk.Dec {
|
|
||||||
return sdk.ZeroDec()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetCommission() sdk.Dec {
|
|
||||||
return sdk.ZeroDec()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetJailed() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetBondHeight() int64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetMoniker() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
func (v Validator) GetDelegatorShareExRate() sdk.Dec {
|
|
||||||
return sdk.ZeroDec()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.Validator
|
|
||||||
type ValidatorSet struct {
|
|
||||||
Validators []Validator
|
|
||||||
}
|
|
||||||
|
|
||||||
// IterateValidators implements sdk.ValidatorSet
|
|
||||||
func (vs *ValidatorSet) IterateValidators(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) {
|
|
||||||
for i, val := range vs.Validators {
|
|
||||||
if fn(int64(i), val) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IterateBondedValidatorsByPower implements sdk.ValidatorSet
|
|
||||||
func (vs *ValidatorSet) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) {
|
|
||||||
vs.IterateValidators(ctx, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IterateLastValidators implements sdk.ValidatorSet
|
|
||||||
func (vs *ValidatorSet) IterateLastValidators(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) {
|
|
||||||
vs.IterateValidators(ctx, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validator implements sdk.ValidatorSet
|
|
||||||
func (vs *ValidatorSet) Validator(ctx sdk.Context, addr sdk.ValAddress) sdk.Validator {
|
|
||||||
for _, val := range vs.Validators {
|
|
||||||
if bytes.Equal(val.Address.Bytes(), addr.Bytes()) {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidatorByPubKey implements sdk.ValidatorSet
|
|
||||||
func (vs *ValidatorSet) ValidatorByConsPubKey(_ sdk.Context, _ crypto.PubKey) sdk.Validator {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidatorByPubKey implements sdk.ValidatorSet
|
|
||||||
func (vs *ValidatorSet) ValidatorByConsAddr(_ sdk.Context, _ sdk.ConsAddress) sdk.Validator {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TotalPower implements sdk.ValidatorSet
|
|
||||||
func (vs *ValidatorSet) TotalPower(ctx sdk.Context) sdk.Int {
|
|
||||||
res := sdk.ZeroInt()
|
|
||||||
for _, val := range vs.Validators {
|
|
||||||
res = res.Add(val.Power)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function for adding new validator
|
|
||||||
func (vs *ValidatorSet) AddValidator(val Validator) {
|
|
||||||
vs.Validators = append(vs.Validators, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function for removing exsting validator
|
|
||||||
func (vs *ValidatorSet) RemoveValidator(addr sdk.AccAddress) {
|
|
||||||
pos := -1
|
|
||||||
for i, val := range vs.Validators {
|
|
||||||
if bytes.Equal(val.Address, addr) {
|
|
||||||
pos = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if pos == -1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vs.Validators = append(vs.Validators[:pos], vs.Validators[pos+1:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.ValidatorSet
|
|
||||||
func (vs *ValidatorSet) Slash(_ sdk.Context, _ sdk.ConsAddress, _ int64, _ int64, _ sdk.Dec) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.ValidatorSet
|
|
||||||
func (vs *ValidatorSet) Jail(_ sdk.Context, _ sdk.ConsAddress) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.ValidatorSet
|
|
||||||
func (vs *ValidatorSet) Unjail(_ sdk.Context, _ sdk.ConsAddress) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.ValidatorSet
|
|
||||||
func (vs *ValidatorSet) Delegation(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) sdk.Delegation {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/pow"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ auth.Account = (*AppAccount)(nil)
|
|
||||||
|
|
||||||
// Custom extensions for this application. This is just an example of
|
|
||||||
// extending auth.BaseAccount with custom fields.
|
|
||||||
//
|
|
||||||
// This is compatible with the stock auth.AccountStore, since
|
|
||||||
// auth.AccountStore uses the flexible go-amino library.
|
|
||||||
type AppAccount struct {
|
|
||||||
auth.BaseAccount
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor for AppAccount
|
|
||||||
func ProtoAppAccount() auth.Account {
|
|
||||||
return &AppAccount{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint
|
|
||||||
func (acc AppAccount) GetName() string { return acc.Name }
|
|
||||||
func (acc *AppAccount) SetName(name string) { acc.Name = name }
|
|
||||||
|
|
||||||
// Get the AccountDecoder function for the custom AppAccount
|
|
||||||
func GetAccountDecoder(cdc *codec.Codec) auth.AccountDecoder {
|
|
||||||
return func(accBytes []byte) (res auth.Account, err error) {
|
|
||||||
if len(accBytes) == 0 {
|
|
||||||
return nil, sdk.ErrTxDecode("accBytes are empty")
|
|
||||||
}
|
|
||||||
acct := new(AppAccount)
|
|
||||||
err = cdc.UnmarshalBinaryBare(accBytes, &acct)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return acct, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//___________________________________________________________________________________
|
|
||||||
|
|
||||||
// State to Unmarshal
|
|
||||||
type GenesisState struct {
|
|
||||||
Accounts []*GenesisAccount `json:"accounts"`
|
|
||||||
POWGenesis pow.Genesis `json:"pow"`
|
|
||||||
CoolGenesis cool.Genesis `json:"cool"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenesisAccount doesn't need pubkey or sequence
|
|
||||||
type GenesisAccount struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Address sdk.AccAddress `json:"address"`
|
|
||||||
Coins sdk.Coins `json:"coins"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
|
|
||||||
return &GenesisAccount{
|
|
||||||
Name: aa.Name,
|
|
||||||
Address: aa.Address,
|
|
||||||
Coins: aa.Coins.Sort(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert GenesisAccount to AppAccount
|
|
||||||
func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) {
|
|
||||||
baseAcc := auth.BaseAccount{
|
|
||||||
Address: ga.Address,
|
|
||||||
Coins: ga.Coins.Sort(),
|
|
||||||
}
|
|
||||||
return &AppAccount{
|
|
||||||
BaseAccount: baseAcc,
|
|
||||||
Name: ga.Name,
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
package assoc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ValidatorSet defines
|
|
||||||
type ValidatorSet struct {
|
|
||||||
sdk.ValidatorSet
|
|
||||||
|
|
||||||
store sdk.KVStore
|
|
||||||
cdc *codec.Codec
|
|
||||||
|
|
||||||
maxAssoc int
|
|
||||||
addrLen int
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ sdk.ValidatorSet = ValidatorSet{}
|
|
||||||
|
|
||||||
// NewValidatorSet returns new ValidatorSet with underlying ValidatorSet
|
|
||||||
func NewValidatorSet(cdc *codec.Codec, store sdk.KVStore, valset sdk.ValidatorSet, maxAssoc int, addrLen int) ValidatorSet {
|
|
||||||
if maxAssoc < 0 || addrLen < 0 {
|
|
||||||
panic("Cannot use negative integer for NewValidatorSet")
|
|
||||||
}
|
|
||||||
return ValidatorSet{
|
|
||||||
ValidatorSet: valset,
|
|
||||||
|
|
||||||
store: store,
|
|
||||||
cdc: cdc,
|
|
||||||
|
|
||||||
maxAssoc: maxAssoc,
|
|
||||||
addrLen: addrLen,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.ValidatorSet
|
|
||||||
func (valset ValidatorSet) Validator(ctx sdk.Context, addr sdk.ValAddress) (res sdk.Validator) {
|
|
||||||
base := valset.store.Get(GetBaseKey(addr))
|
|
||||||
res = valset.ValidatorSet.Validator(ctx, base)
|
|
||||||
if res == nil {
|
|
||||||
res = valset.ValidatorSet.Validator(ctx, addr)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBaseKey :: sdk.ValAddress -> sdk.ValAddress
|
|
||||||
func GetBaseKey(addr sdk.ValAddress) []byte {
|
|
||||||
return append([]byte{0x00}, addr...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAssocPrefix :: sdk.ValAddress -> (sdk.ValAddress -> byte)
|
|
||||||
func GetAssocPrefix(base sdk.ValAddress) []byte {
|
|
||||||
return append([]byte{0x01}, base...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAssocKey :: (sdk.ValAddress, sdk.ValAddress) -> byte
|
|
||||||
func GetAssocKey(base sdk.ValAddress, assoc sdk.ValAddress) []byte {
|
|
||||||
return append(append([]byte{0x01}, base...), assoc...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Associate associates new address with validator address
|
|
||||||
// nolint: unparam
|
|
||||||
func (valset ValidatorSet) Associate(ctx sdk.Context, base sdk.ValAddress, assoc sdk.ValAddress) bool {
|
|
||||||
if len(base) != valset.addrLen || len(assoc) != valset.addrLen {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// If someone already owns the associated address
|
|
||||||
if valset.store.Get(GetBaseKey(assoc)) != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
valset.store.Set(GetBaseKey(assoc), base)
|
|
||||||
valset.store.Set(GetAssocKey(base, assoc), []byte{0x00})
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dissociate removes association between addresses
|
|
||||||
// nolint: unparam
|
|
||||||
func (valset ValidatorSet) Dissociate(ctx sdk.Context, base sdk.ValAddress, assoc sdk.ValAddress) bool {
|
|
||||||
if len(base) != valset.addrLen || len(assoc) != valset.addrLen {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// No associated address found for given validator
|
|
||||||
if !bytes.Equal(valset.store.Get(GetBaseKey(assoc)), base) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
valset.store.Delete(GetBaseKey(assoc))
|
|
||||||
valset.store.Delete(GetAssocKey(base, assoc))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Associations returns all associated addresses with a validator
|
|
||||||
// nolint: unparam
|
|
||||||
func (valset ValidatorSet) Associations(ctx sdk.Context, base sdk.ValAddress) (res []sdk.ValAddress) {
|
|
||||||
res = make([]sdk.ValAddress, valset.maxAssoc)
|
|
||||||
iter := sdk.KVStorePrefixIterator(valset.store, GetAssocPrefix(base))
|
|
||||||
defer iter.Close()
|
|
||||||
i := 0
|
|
||||||
for ; iter.Valid(); iter.Next() {
|
|
||||||
key := iter.Key()
|
|
||||||
res[i] = key[len(key)-valset.addrLen:]
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return res[:i]
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
package assoc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/mock"
|
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func defaultContext(key sdk.StoreKey) sdk.Context {
|
|
||||||
db := dbm.NewMemDB()
|
|
||||||
cms := store.NewCommitMultiStore(db)
|
|
||||||
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
|
|
||||||
cms.LoadLatestVersion()
|
|
||||||
ctx := sdk.NewContext(cms, abci.Header{}, false, nil)
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidatorSet(t *testing.T) {
|
|
||||||
key := sdk.NewKVStoreKey("test")
|
|
||||||
ctx := defaultContext(key)
|
|
||||||
|
|
||||||
addr1 := []byte("addr1")
|
|
||||||
addr2 := []byte("addr2")
|
|
||||||
|
|
||||||
base := &mock.ValidatorSet{[]mock.Validator{
|
|
||||||
{addr1, sdk.NewInt(1)},
|
|
||||||
{addr2, sdk.NewInt(2)},
|
|
||||||
}}
|
|
||||||
|
|
||||||
valset := NewValidatorSet(codec.New(), ctx.KVStore(key).Prefix([]byte("assoc")), base, 1, 5)
|
|
||||||
|
|
||||||
require.Equal(t, base.Validator(ctx, addr1), valset.Validator(ctx, addr1))
|
|
||||||
require.Equal(t, base.Validator(ctx, addr2), valset.Validator(ctx, addr2))
|
|
||||||
|
|
||||||
assoc1 := []byte("asso1")
|
|
||||||
assoc2 := []byte("asso2")
|
|
||||||
|
|
||||||
require.True(t, valset.Associate(ctx, addr1, assoc1))
|
|
||||||
require.True(t, valset.Associate(ctx, addr2, assoc2))
|
|
||||||
|
|
||||||
require.Equal(t, base.Validator(ctx, addr1), valset.Validator(ctx, assoc1))
|
|
||||||
require.Equal(t, base.Validator(ctx, addr2), valset.Validator(ctx, assoc2))
|
|
||||||
|
|
||||||
require.Equal(t, base.Validator(ctx, addr1), valset.Validator(ctx, addr1))
|
|
||||||
require.Equal(t, base.Validator(ctx, addr2), valset.Validator(ctx, addr2))
|
|
||||||
|
|
||||||
assocs := valset.Associations(ctx, addr1)
|
|
||||||
require.Equal(t, 1, len(assocs))
|
|
||||||
require.True(t, bytes.Equal(assoc1, assocs[0]))
|
|
||||||
|
|
||||||
require.False(t, valset.Associate(ctx, addr1, assoc2))
|
|
||||||
require.False(t, valset.Associate(ctx, addr2, assoc1))
|
|
||||||
|
|
||||||
valset.Dissociate(ctx, addr1, assoc1)
|
|
||||||
valset.Dissociate(ctx, addr2, assoc2)
|
|
||||||
|
|
||||||
require.Equal(t, base.Validator(ctx, addr1), valset.Validator(ctx, addr1))
|
|
||||||
require.Equal(t, base.Validator(ctx, addr2), valset.Validator(ctx, addr2))
|
|
||||||
|
|
||||||
require.Nil(t, valset.Validator(ctx, assoc1))
|
|
||||||
require.Nil(t, valset.Validator(ctx, assoc2))
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
package cool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
bank "github.com/cosmos/cosmos-sdk/x/bank"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
priv1 = ed25519.GenPrivKey()
|
|
||||||
pubKey = priv1.PubKey()
|
|
||||||
addr1 = sdk.AccAddress(pubKey.Address())
|
|
||||||
|
|
||||||
quizMsg1 = MsgQuiz{
|
|
||||||
Sender: addr1,
|
|
||||||
CoolAnswer: "icecold",
|
|
||||||
}
|
|
||||||
|
|
||||||
quizMsg2 = MsgQuiz{
|
|
||||||
Sender: addr1,
|
|
||||||
CoolAnswer: "badvibesonly",
|
|
||||||
}
|
|
||||||
|
|
||||||
setTrendMsg1 = MsgSetTrend{
|
|
||||||
Sender: addr1,
|
|
||||||
Cool: "icecold",
|
|
||||||
}
|
|
||||||
|
|
||||||
setTrendMsg2 = MsgSetTrend{
|
|
||||||
Sender: addr1,
|
|
||||||
Cool: "badvibesonly",
|
|
||||||
}
|
|
||||||
|
|
||||||
setTrendMsg3 = MsgSetTrend{
|
|
||||||
Sender: addr1,
|
|
||||||
Cool: "warmandkind",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// initialize the mock application for this module
|
|
||||||
func getMockApp(t *testing.T) *mock.App {
|
|
||||||
mapp := mock.NewApp()
|
|
||||||
|
|
||||||
RegisterCodec(mapp.Cdc)
|
|
||||||
keyCool := sdk.NewKVStoreKey("cool")
|
|
||||||
bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper)
|
|
||||||
keeper := NewKeeper(keyCool, bankKeeper, DefaultCodespace)
|
|
||||||
mapp.Router().AddRoute("cool", NewHandler(keeper))
|
|
||||||
|
|
||||||
mapp.SetInitChainer(getInitChainer(mapp, keeper, "ice-cold"))
|
|
||||||
|
|
||||||
require.NoError(t, mapp.CompleteSetup(keyCool))
|
|
||||||
return mapp
|
|
||||||
}
|
|
||||||
|
|
||||||
// overwrite the mock init chainer
|
|
||||||
func getInitChainer(mapp *mock.App, keeper Keeper, newTrend string) sdk.InitChainer {
|
|
||||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
|
||||||
mapp.InitChainer(ctx, req)
|
|
||||||
keeper.setTrend(ctx, newTrend)
|
|
||||||
|
|
||||||
return abci.ResponseInitChain{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMsgQuiz(t *testing.T) {
|
|
||||||
mapp := getMockApp(t)
|
|
||||||
|
|
||||||
// Construct genesis state
|
|
||||||
acc1 := &auth.BaseAccount{
|
|
||||||
Address: addr1,
|
|
||||||
Coins: nil,
|
|
||||||
}
|
|
||||||
accs := []auth.Account{acc1}
|
|
||||||
|
|
||||||
// Initialize the chain (nil)
|
|
||||||
mock.SetGenesis(mapp, accs)
|
|
||||||
|
|
||||||
// A checkTx context (true)
|
|
||||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
|
||||||
res1 := mapp.AccountKeeper.GetAccount(ctxCheck, addr1)
|
|
||||||
require.Equal(t, acc1, res1)
|
|
||||||
|
|
||||||
// Set the trend, submit a really cool quiz and check for reward
|
|
||||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg1}, []uint64{0}, []uint64{0}, true, true, priv1)
|
|
||||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []uint64{0}, []uint64{1}, true, true, priv1)
|
|
||||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(69))})
|
|
||||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []uint64{0}, []uint64{2}, false, false, priv1) // result without reward
|
|
||||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(69))})
|
|
||||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []uint64{0}, []uint64{3}, true, true, priv1)
|
|
||||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(138))})
|
|
||||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []uint64{0}, []uint64{4}, true, true, priv1) // reset the trend
|
|
||||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []uint64{0}, []uint64{5}, false, false, priv1) // the same answer will nolonger do!
|
|
||||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(138))})
|
|
||||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []uint64{0}, []uint64{6}, true, true, priv1) // earlier answer now relevant again
|
|
||||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("badvibesonly", sdk.NewInt(69)), sdk.NewCoin("icecold", sdk.NewInt(138))})
|
|
||||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []uint64{0}, []uint64{7}, false, false, priv1) // expect to fail to set the trend to something which is not cool
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/utils"
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
|
||||||
)
|
|
||||||
|
|
||||||
// QuizTxCmd invokes the coolness quiz transaction.
|
|
||||||
func QuizTxCmd(cdc *codec.Codec) *cobra.Command {
|
|
||||||
return &cobra.Command{
|
|
||||||
Use: "cool [answer]",
|
|
||||||
Short: "What's cooler than being cool?",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
|
|
||||||
cliCtx := context.NewCLIContext().
|
|
||||||
WithCodec(cdc).
|
|
||||||
WithAccountDecoder(cdc)
|
|
||||||
|
|
||||||
from, err := cliCtx.GetFromAddress()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := cool.NewMsgQuiz(from, args[0])
|
|
||||||
|
|
||||||
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTrendTxCmd sends a new cool trend transaction.
|
|
||||||
func SetTrendTxCmd(cdc *codec.Codec) *cobra.Command {
|
|
||||||
return &cobra.Command{
|
|
||||||
Use: "setcool [answer]",
|
|
||||||
Short: "You're so cool, tell us what is cool!",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
|
|
||||||
cliCtx := context.NewCLIContext().
|
|
||||||
WithCodec(cdc).
|
|
||||||
WithAccountDecoder(cdc)
|
|
||||||
|
|
||||||
from, err := cliCtx.GetFromAddress()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := cool.NewMsgSetTrend(from, args[0])
|
|
||||||
|
|
||||||
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package cool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Register concrete types on codec codec
|
|
||||||
func RegisterCodec(cdc *codec.Codec) {
|
|
||||||
cdc.RegisterConcrete(MsgQuiz{}, "cool/Quiz", nil)
|
|
||||||
cdc.RegisterConcrete(MsgSetTrend{}, "cool/SetTrend", nil)
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package cool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Cool errors reserve 400 ~ 499.
|
|
||||||
const (
|
|
||||||
DefaultCodespace sdk.CodespaceType = "cool"
|
|
||||||
|
|
||||||
// Cool module reserves error 400-499 lawl
|
|
||||||
CodeIncorrectCoolAnswer sdk.CodeType = 400
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrIncorrectCoolAnswer - Error returned upon an incorrect guess
|
|
||||||
func ErrIncorrectCoolAnswer(codespace sdk.CodespaceType, answer string) sdk.Error {
|
|
||||||
return sdk.NewError(codespace, CodeIncorrectCoolAnswer, fmt.Sprintf("incorrect cool answer: %v", answer))
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package cool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is just an example to demonstrate a functional custom module
|
|
||||||
// with full feature set functionality.
|
|
||||||
//
|
|
||||||
// /$$$$$$$ /$$$$$$ /$$$$$$ /$$
|
|
||||||
// /$$_____/ /$$__ $$ /$$__ $$| $$
|
|
||||||
//| $$ | $$ \ $$| $$ \ $$| $$
|
|
||||||
//| $$ | $$ | $$| $$ | $$| $$
|
|
||||||
//| $$$$$$$| $$$$$$/| $$$$$$/| $$$$$$$
|
|
||||||
// \_______/ \______/ \______/ |______/
|
|
||||||
|
|
||||||
// NewHandler returns a handler for "cool" type messages.
|
|
||||||
func NewHandler(k Keeper) sdk.Handler {
|
|
||||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case MsgSetTrend:
|
|
||||||
return handleMsgSetTrend(ctx, k, msg)
|
|
||||||
case MsgQuiz:
|
|
||||||
return handleMsgQuiz(ctx, k, msg)
|
|
||||||
default:
|
|
||||||
errMsg := fmt.Sprintf("Unrecognized cool Msg type: %v", msg.Type())
|
|
||||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle MsgQuiz This is the engine of your module
|
|
||||||
func handleMsgSetTrend(ctx sdk.Context, k Keeper, msg MsgSetTrend) sdk.Result {
|
|
||||||
k.setTrend(ctx, msg.Cool)
|
|
||||||
return sdk.Result{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle MsgQuiz This is the engine of your module
|
|
||||||
func handleMsgQuiz(ctx sdk.Context, k Keeper, msg MsgQuiz) sdk.Result {
|
|
||||||
|
|
||||||
correct := k.CheckTrend(ctx, msg.CoolAnswer)
|
|
||||||
|
|
||||||
if !correct {
|
|
||||||
return ErrIncorrectCoolAnswer(k.codespace, msg.CoolAnswer).Result()
|
|
||||||
}
|
|
||||||
|
|
||||||
bonusCoins := sdk.Coins{sdk.NewInt64Coin(msg.CoolAnswer, 69)}
|
|
||||||
|
|
||||||
_, _, err := k.ck.AddCoins(ctx, msg.Sender, bonusCoins)
|
|
||||||
if err != nil {
|
|
||||||
return err.Result()
|
|
||||||
}
|
|
||||||
|
|
||||||
return sdk.Result{}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package cool
|
|
||||||
|
|
||||||
import (
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Keeper - handlers sets/gets of custom variables for your module
|
|
||||||
type Keeper struct {
|
|
||||||
ck bank.Keeper
|
|
||||||
|
|
||||||
storeKey sdk.StoreKey // The (unexposed) key used to access the store from the Context.
|
|
||||||
|
|
||||||
codespace sdk.CodespaceType
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewKeeper - Returns the Keeper
|
|
||||||
func NewKeeper(key sdk.StoreKey, bankKeeper bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
|
||||||
return Keeper{bankKeeper, key, codespace}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key to knowing the trend on the streets!
|
|
||||||
var trendKey = []byte("TrendKey")
|
|
||||||
|
|
||||||
// GetTrend - returns the current cool trend
|
|
||||||
func (k Keeper) GetTrend(ctx sdk.Context) string {
|
|
||||||
store := ctx.KVStore(k.storeKey)
|
|
||||||
bz := store.Get(trendKey)
|
|
||||||
return string(bz)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements sdk.AccountKeeper.
|
|
||||||
func (k Keeper) setTrend(ctx sdk.Context, newTrend string) {
|
|
||||||
store := ctx.KVStore(k.storeKey)
|
|
||||||
store.Set(trendKey, []byte(newTrend))
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckTrend - Returns true or false based on whether guessedTrend is currently cool or not
|
|
||||||
func (k Keeper) CheckTrend(ctx sdk.Context, guessedTrend string) bool {
|
|
||||||
if guessedTrend == k.GetTrend(ctx) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitGenesis - store the genesis trend
|
|
||||||
func InitGenesis(ctx sdk.Context, k Keeper, data Genesis) error {
|
|
||||||
k.setTrend(ctx, data.Trend)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportGenesis - output the genesis trend
|
|
||||||
func ExportGenesis(ctx sdk.Context, k Keeper) Genesis {
|
|
||||||
trend := k.GetTrend(ctx)
|
|
||||||
return Genesis{trend}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
package cool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"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"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testInput struct {
|
|
||||||
cdc *codec.Codec
|
|
||||||
ctx sdk.Context
|
|
||||||
capKey *sdk.KVStoreKey
|
|
||||||
bk bank.BaseKeeper
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupTestInput() testInput {
|
|
||||||
db := dbm.NewMemDB()
|
|
||||||
|
|
||||||
cdc := codec.New()
|
|
||||||
auth.RegisterBaseAccount(cdc)
|
|
||||||
|
|
||||||
capKey := sdk.NewKVStoreKey("capkey")
|
|
||||||
keyParams := sdk.NewKVStoreKey("params")
|
|
||||||
tkeyParams := sdk.NewTransientStoreKey("transient_params")
|
|
||||||
|
|
||||||
ms := store.NewCommitMultiStore(db)
|
|
||||||
ms.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db)
|
|
||||||
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
|
|
||||||
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
|
|
||||||
ms.LoadLatestVersion()
|
|
||||||
|
|
||||||
pk := params.NewKeeper(cdc, keyParams, tkeyParams)
|
|
||||||
ak := auth.NewAccountKeeper(cdc, capKey, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount)
|
|
||||||
bk := bank.NewBaseKeeper(ak)
|
|
||||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
|
||||||
|
|
||||||
return testInput{cdc: cdc, ctx: ctx, capKey: capKey, bk: bk}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCoolKeeper(t *testing.T) {
|
|
||||||
input := setupTestInput()
|
|
||||||
keeper := NewKeeper(input.capKey, input.bk, DefaultCodespace)
|
|
||||||
ctx := input.ctx
|
|
||||||
|
|
||||||
err := InitGenesis(ctx, keeper, Genesis{"icy"})
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
genesis := ExportGenesis(ctx, keeper)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Equal(t, genesis, Genesis{"icy"})
|
|
||||||
|
|
||||||
res := keeper.GetTrend(ctx)
|
|
||||||
require.Equal(t, res, "icy")
|
|
||||||
|
|
||||||
keeper.setTrend(ctx, "fiery")
|
|
||||||
res = keeper.GetTrend(ctx)
|
|
||||||
require.Equal(t, res, "fiery")
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
package cool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// a really cool msg type, these fields are can be entirely arbitrary and
|
|
||||||
// custom to your message
|
|
||||||
type MsgSetTrend struct {
|
|
||||||
Sender sdk.AccAddress
|
|
||||||
Cool string
|
|
||||||
}
|
|
||||||
|
|
||||||
// genesis state - specify genesis trend
|
|
||||||
type Genesis struct {
|
|
||||||
Trend string `json:"trend"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// new cool message
|
|
||||||
func NewMsgSetTrend(sender sdk.AccAddress, cool string) MsgSetTrend {
|
|
||||||
return MsgSetTrend{
|
|
||||||
Sender: sender,
|
|
||||||
Cool: cool,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// enforce the msg type at compile time
|
|
||||||
var _ sdk.Msg = MsgSetTrend{}
|
|
||||||
|
|
||||||
// nolint
|
|
||||||
func (msg MsgSetTrend) Route() string { return "cool" }
|
|
||||||
func (msg MsgSetTrend) Type() string { return "set_trend" }
|
|
||||||
func (msg MsgSetTrend) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Sender} }
|
|
||||||
func (msg MsgSetTrend) String() string {
|
|
||||||
return fmt.Sprintf("MsgSetTrend{Sender: %v, Cool: %v}", msg.Sender, msg.Cool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
|
|
||||||
func (msg MsgSetTrend) ValidateBasic() sdk.Error {
|
|
||||||
if len(msg.Sender) == 0 {
|
|
||||||
return sdk.ErrUnknownAddress(msg.Sender.String()).TraceSDK("")
|
|
||||||
}
|
|
||||||
if strings.Contains(msg.Cool, "hot") {
|
|
||||||
return sdk.ErrUnauthorized("").TraceSDK("hot is not cool")
|
|
||||||
}
|
|
||||||
if strings.Contains(msg.Cool, "warm") {
|
|
||||||
return sdk.ErrUnauthorized("").TraceSDK("warm is not very cool")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the bytes for the message signer to sign on
|
|
||||||
func (msg MsgSetTrend) GetSignBytes() []byte {
|
|
||||||
b, err := json.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return sdk.MustSortJSON(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
//_______________________________________________________________________
|
|
||||||
|
|
||||||
// A message type to quiz how cool you are. these fields are can be entirely
|
|
||||||
// arbitrary and custom to your message
|
|
||||||
type MsgQuiz struct {
|
|
||||||
Sender sdk.AccAddress
|
|
||||||
CoolAnswer string
|
|
||||||
}
|
|
||||||
|
|
||||||
// New cool message
|
|
||||||
func NewMsgQuiz(sender sdk.AccAddress, coolerthancool string) MsgQuiz {
|
|
||||||
return MsgQuiz{
|
|
||||||
Sender: sender,
|
|
||||||
CoolAnswer: coolerthancool,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// enforce the msg type at compile time
|
|
||||||
var _ sdk.Msg = MsgQuiz{}
|
|
||||||
|
|
||||||
// nolint
|
|
||||||
func (msg MsgQuiz) Route() string { return "cool" }
|
|
||||||
func (msg MsgQuiz) Type() string { return "quiz" }
|
|
||||||
func (msg MsgQuiz) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Sender} }
|
|
||||||
func (msg MsgQuiz) String() string {
|
|
||||||
return fmt.Sprintf("MsgQuiz{Sender: %v, CoolAnswer: %v}", msg.Sender, msg.CoolAnswer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
|
|
||||||
func (msg MsgQuiz) ValidateBasic() sdk.Error {
|
|
||||||
if len(msg.Sender) == 0 {
|
|
||||||
return sdk.ErrUnknownAddress(msg.Sender.String()).TraceSDK("")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the bytes for the message signer to sign on
|
|
||||||
func (msg MsgQuiz) GetSignBytes() []byte {
|
|
||||||
b, err := json.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return sdk.MustSortJSON(b)
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
# Oracle Module
|
|
||||||
|
|
||||||
`x/oracle` provides a way to receive external information(real world price, events from other chains, etc.) with validators' vote. Each validator make transaction which contains those informations, and Oracle aggregates them until the supermajority signed on it. After then, Oracle sends the information to the actual module that processes the information, and prune the votes from the state.
|
|
||||||
|
|
||||||
## Integration
|
|
||||||
|
|
||||||
See `x/oracle/oracle_test.go` for the code that using Oracle
|
|
||||||
|
|
||||||
To use Oracle in your module, first define a `payload`. It should implement `oracle.Payload` and contain nessesary information for your module. Including nonce is recommended.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type MyPayload struct {
|
|
||||||
Data int
|
|
||||||
Nonce int
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When you write a payload, its `.Route()` should return same route with your module is registered on the router. It is because `oracle.Msg` inherits `.Route()` from its embedded payload and it should be handled on the user modules.
|
|
||||||
|
|
||||||
Then route every incoming `oracle.Msg` to `oracle.Keeper.Handler()` with the function that implements `oracle.Handler`.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewHandler(keeper Keeper) sdk.Handler {
|
|
||||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case oracle.Msg:
|
|
||||||
return keeper.oracle.Handle(ctx sdk.Context, p oracle.Payload) sdk.Error {
|
|
||||||
switch p := p.(type) {
|
|
||||||
case MyPayload:
|
|
||||||
return handleMyPayload(ctx, keeper, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In the previous example, the keeper has an `oracle.Keeper`. `oracle.Keeper`s are generated by `NewKeeper`.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewKeeper(key sdk.StoreKey, cdc *codec.Codec, valset sdk.ValidatorSet, supermaj sdk.Dec, timeout int64) Keeper {
|
|
||||||
return Keeper {
|
|
||||||
cdc: cdc,
|
|
||||||
key: key,
|
|
||||||
|
|
||||||
// ValidatorSet to get validators infor
|
|
||||||
valset: valset,
|
|
||||||
|
|
||||||
// The keeper will pass payload
|
|
||||||
// when more than 2/3 signed on it
|
|
||||||
supermaj: supermaj,
|
|
||||||
// The keeper will prune votes after 100 blocks from last sign
|
|
||||||
timeout: timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Now the validators can send `oracle.Msg`s with `MyPayload` when they want to witness external events.
|
|
|
@ -1,31 +0,0 @@
|
||||||
package oracle
|
|
||||||
|
|
||||||
import (
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Oracle errors reserve 1101-1199
|
|
||||||
const (
|
|
||||||
CodeNotValidator sdk.CodeType = 1101
|
|
||||||
CodeAlreadyProcessed sdk.CodeType = 1102
|
|
||||||
CodeAlreadySigned sdk.CodeType = 1103
|
|
||||||
CodeUnknownRequest sdk.CodeType = sdk.CodeUnknownRequest
|
|
||||||
)
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
// Error constructors
|
|
||||||
|
|
||||||
// ErrNotValidator called when the signer of a Msg is not a validator
|
|
||||||
func ErrNotValidator(codespace sdk.CodespaceType, address sdk.AccAddress) sdk.Error {
|
|
||||||
return sdk.NewError(codespace, CodeNotValidator, address.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrAlreadyProcessed called when a payload is already processed
|
|
||||||
func ErrAlreadyProcessed(codespace sdk.CodespaceType) sdk.Error {
|
|
||||||
return sdk.NewError(codespace, CodeAlreadyProcessed, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrAlreadySigned called when the signer is trying to double signing
|
|
||||||
func ErrAlreadySigned(codespace sdk.CodespaceType) sdk.Error {
|
|
||||||
return sdk.NewError(codespace, CodeAlreadySigned, "")
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
package oracle
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handler handles payload after it passes voting process
|
|
||||||
type Handler func(ctx sdk.Context, p Payload) sdk.Error
|
|
||||||
|
|
||||||
func (keeper Keeper) update(ctx sdk.Context, val sdk.Validator, valset sdk.ValidatorSet, p Payload, info Info) Info {
|
|
||||||
info.Power = info.Power.Add(val.GetPower())
|
|
||||||
|
|
||||||
// Return if the voted power is not bigger than required power
|
|
||||||
totalPower := valset.TotalPower(ctx)
|
|
||||||
requiredPower := keeper.supermaj.MulInt(totalPower).RoundInt()
|
|
||||||
if !info.Power.GT(requiredPower) {
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the validators hash has been changed during the vote process
|
|
||||||
// and recalculate voted power
|
|
||||||
hash := ctx.BlockHeader().ValidatorsHash
|
|
||||||
if !bytes.Equal(hash, info.Hash) {
|
|
||||||
info.Power = sdk.ZeroInt()
|
|
||||||
info.Hash = hash
|
|
||||||
prefix := GetSignPrefix(p, keeper.cdc)
|
|
||||||
store := ctx.KVStore(keeper.key)
|
|
||||||
iter := sdk.KVStorePrefixIterator(store, prefix)
|
|
||||||
defer iter.Close()
|
|
||||||
for ; iter.Valid(); iter.Next() {
|
|
||||||
if valset.Validator(ctx, iter.Value()) != nil {
|
|
||||||
store.Delete(iter.Key())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
info.Power = info.Power.Add(val.GetPower())
|
|
||||||
}
|
|
||||||
if !info.Power.GT(keeper.supermaj.MulInt(totalPower).RoundInt()) {
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
info.Status = Processed
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle is used by other modules to handle Msg
|
|
||||||
func (keeper Keeper) Handle(h Handler, ctx sdk.Context, o Msg, codespace sdk.CodespaceType) sdk.Result {
|
|
||||||
valset := keeper.valset
|
|
||||||
|
|
||||||
signer := o.Signer
|
|
||||||
payload := o.Payload
|
|
||||||
|
|
||||||
// Check the oracle is not in process
|
|
||||||
info := keeper.Info(ctx, payload)
|
|
||||||
if info.Status != Pending {
|
|
||||||
return ErrAlreadyProcessed(codespace).Result()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it is reporting timeout
|
|
||||||
now := ctx.BlockHeight()
|
|
||||||
if now > info.LastSigned+keeper.timeout {
|
|
||||||
info = Info{Status: Timeout}
|
|
||||||
keeper.setInfo(ctx, payload, info)
|
|
||||||
keeper.clearSigns(ctx, payload)
|
|
||||||
return sdk.Result{}
|
|
||||||
}
|
|
||||||
info.LastSigned = ctx.BlockHeight()
|
|
||||||
|
|
||||||
// check the signer is a validator
|
|
||||||
val := valset.Validator(ctx, sdk.ValAddress(signer))
|
|
||||||
if val == nil {
|
|
||||||
return ErrNotValidator(codespace, signer).Result()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check double signing
|
|
||||||
if keeper.signed(ctx, payload, signer) {
|
|
||||||
return ErrAlreadySigned(codespace).Result()
|
|
||||||
}
|
|
||||||
|
|
||||||
keeper.sign(ctx, payload, signer)
|
|
||||||
|
|
||||||
info = keeper.update(ctx, val, valset, payload, info)
|
|
||||||
if info.Status == Processed {
|
|
||||||
info = Info{Status: Processed}
|
|
||||||
}
|
|
||||||
|
|
||||||
keeper.setInfo(ctx, payload, info)
|
|
||||||
|
|
||||||
if info.Status == Processed {
|
|
||||||
keeper.clearSigns(ctx, payload)
|
|
||||||
cctx, write := ctx.CacheContext()
|
|
||||||
err := h(cctx, payload)
|
|
||||||
if err != nil {
|
|
||||||
return sdk.Result{
|
|
||||||
Code: sdk.CodeOK,
|
|
||||||
Log: err.ABCILog(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return sdk.Result{}
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
package oracle
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Keeper of the oracle store
|
|
||||||
type Keeper struct {
|
|
||||||
key sdk.StoreKey
|
|
||||||
cdc *codec.Codec
|
|
||||||
|
|
||||||
valset sdk.ValidatorSet
|
|
||||||
|
|
||||||
supermaj sdk.Dec
|
|
||||||
timeout int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewKeeper constructs a new keeper
|
|
||||||
func NewKeeper(key sdk.StoreKey, cdc *codec.Codec, valset sdk.ValidatorSet, supermaj sdk.Dec, timeout int64) Keeper {
|
|
||||||
if timeout < 0 {
|
|
||||||
panic("Timeout should not be negative")
|
|
||||||
}
|
|
||||||
|
|
||||||
return Keeper{
|
|
||||||
key: key,
|
|
||||||
cdc: cdc,
|
|
||||||
|
|
||||||
valset: valset,
|
|
||||||
|
|
||||||
supermaj: supermaj,
|
|
||||||
timeout: timeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// InfoStatus - current status of an Info
|
|
||||||
type InfoStatus int8
|
|
||||||
|
|
||||||
// Define InfoStatus
|
|
||||||
const (
|
|
||||||
Pending = InfoStatus(iota)
|
|
||||||
Processed
|
|
||||||
Timeout
|
|
||||||
)
|
|
||||||
|
|
||||||
// Info for each payload
|
|
||||||
type Info struct {
|
|
||||||
Power sdk.Int
|
|
||||||
Hash []byte
|
|
||||||
LastSigned int64
|
|
||||||
Status InfoStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmptyInfo construct an empty Info
|
|
||||||
func EmptyInfo(ctx sdk.Context) Info {
|
|
||||||
return Info{
|
|
||||||
Power: sdk.ZeroInt(),
|
|
||||||
Hash: ctx.BlockHeader().ValidatorsHash,
|
|
||||||
LastSigned: ctx.BlockHeight(),
|
|
||||||
Status: Pending,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info returns the information about a payload
|
|
||||||
func (keeper Keeper) Info(ctx sdk.Context, p Payload) (res Info) {
|
|
||||||
store := ctx.KVStore(keeper.key)
|
|
||||||
|
|
||||||
key := GetInfoKey(p, keeper.cdc)
|
|
||||||
bz := store.Get(key)
|
|
||||||
if bz == nil {
|
|
||||||
return EmptyInfo(ctx)
|
|
||||||
}
|
|
||||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &res)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (keeper Keeper) setInfo(ctx sdk.Context, p Payload, info Info) {
|
|
||||||
store := ctx.KVStore(keeper.key)
|
|
||||||
|
|
||||||
key := GetInfoKey(p, keeper.cdc)
|
|
||||||
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(info)
|
|
||||||
store.Set(key, bz)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (keeper Keeper) sign(ctx sdk.Context, p Payload, signer sdk.AccAddress) {
|
|
||||||
store := ctx.KVStore(keeper.key)
|
|
||||||
|
|
||||||
key := GetSignKey(p, signer, keeper.cdc)
|
|
||||||
store.Set(key, signer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (keeper Keeper) signed(ctx sdk.Context, p Payload, signer sdk.AccAddress) bool {
|
|
||||||
store := ctx.KVStore(keeper.key)
|
|
||||||
|
|
||||||
key := GetSignKey(p, signer, keeper.cdc)
|
|
||||||
return store.Has(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (keeper Keeper) clearSigns(ctx sdk.Context, p Payload) {
|
|
||||||
store := ctx.KVStore(keeper.key)
|
|
||||||
|
|
||||||
prefix := GetSignPrefix(p, keeper.cdc)
|
|
||||||
|
|
||||||
iter := sdk.KVStorePrefixIterator(store, prefix)
|
|
||||||
for ; iter.Valid(); iter.Next() {
|
|
||||||
store.Delete(iter.Key())
|
|
||||||
}
|
|
||||||
iter.Close()
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package oracle
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetInfoKey returns the key for OracleInfo
|
|
||||||
func GetInfoKey(p Payload, cdc *codec.Codec) []byte {
|
|
||||||
bz := cdc.MustMarshalBinaryLengthPrefixed(p)
|
|
||||||
return append([]byte{0x00}, bz...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSignPrefix returns the prefix for signs
|
|
||||||
func GetSignPrefix(p Payload, cdc *codec.Codec) []byte {
|
|
||||||
bz := cdc.MustMarshalBinaryLengthPrefixed(p)
|
|
||||||
return append([]byte{0x01}, bz...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSignKey returns the key for sign
|
|
||||||
func GetSignKey(p Payload, signer sdk.AccAddress, cdc *codec.Codec) []byte {
|
|
||||||
return append(GetSignPrefix(p, cdc), signer...)
|
|
||||||
}
|
|
|
@ -1,225 +0,0 @@
|
||||||
package oracle
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/mock"
|
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func defaultContext(keys ...sdk.StoreKey) sdk.Context {
|
|
||||||
db := dbm.NewMemDB()
|
|
||||||
cms := store.NewCommitMultiStore(db)
|
|
||||||
for _, key := range keys {
|
|
||||||
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
|
|
||||||
}
|
|
||||||
cms.LoadLatestVersion()
|
|
||||||
ctx := sdk.NewContext(cms, abci.Header{}, false, nil)
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
type seqOracle struct {
|
|
||||||
Seq int
|
|
||||||
Nonce int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o seqOracle) Route() string {
|
|
||||||
return "seq"
|
|
||||||
}
|
|
||||||
func (o seqOracle) Type() string {
|
|
||||||
return "seq"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o seqOracle) ValidateBasic() sdk.Error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeCodec() *codec.Codec {
|
|
||||||
var cdc = codec.New()
|
|
||||||
|
|
||||||
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
|
||||||
cdc.RegisterConcrete(Msg{}, "test/Oracle", nil)
|
|
||||||
|
|
||||||
cdc.RegisterInterface((*Payload)(nil), nil)
|
|
||||||
cdc.RegisterConcrete(seqOracle{}, "test/oracle/seqOracle", nil)
|
|
||||||
|
|
||||||
cdc.Seal()
|
|
||||||
|
|
||||||
return cdc
|
|
||||||
}
|
|
||||||
|
|
||||||
func seqHandler(ork Keeper, key sdk.StoreKey, codespace sdk.CodespaceType) sdk.Handler {
|
|
||||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case Msg:
|
|
||||||
return ork.Handle(func(ctx sdk.Context, p Payload) sdk.Error {
|
|
||||||
switch p := p.(type) {
|
|
||||||
case seqOracle:
|
|
||||||
return handleSeqOracle(ctx, key, p)
|
|
||||||
default:
|
|
||||||
return sdk.ErrUnknownRequest("")
|
|
||||||
}
|
|
||||||
}, ctx, msg, codespace)
|
|
||||||
default:
|
|
||||||
return sdk.ErrUnknownRequest("").Result()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSequence(ctx sdk.Context, key sdk.StoreKey) int {
|
|
||||||
store := ctx.KVStore(key)
|
|
||||||
seqbz := store.Get([]byte("seq"))
|
|
||||||
|
|
||||||
var seq int
|
|
||||||
if seqbz == nil {
|
|
||||||
seq = 0
|
|
||||||
} else {
|
|
||||||
codec.New().MustUnmarshalBinaryLengthPrefixed(seqbz, &seq)
|
|
||||||
}
|
|
||||||
|
|
||||||
return seq
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleSeqOracle(ctx sdk.Context, key sdk.StoreKey, o seqOracle) sdk.Error {
|
|
||||||
store := ctx.KVStore(key)
|
|
||||||
|
|
||||||
seq := getSequence(ctx, key)
|
|
||||||
if seq != o.Seq {
|
|
||||||
return sdk.NewError(sdk.CodespaceRoot, 1, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
bz := codec.New().MustMarshalBinaryLengthPrefixed(seq + 1)
|
|
||||||
store.Set([]byte("seq"), bz)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOracle(t *testing.T) {
|
|
||||||
cdc := makeCodec()
|
|
||||||
|
|
||||||
addr1 := []byte("addr1")
|
|
||||||
addr2 := []byte("addr2")
|
|
||||||
addr3 := []byte("addr3")
|
|
||||||
addr4 := []byte("addr4")
|
|
||||||
valset := &mock.ValidatorSet{[]mock.Validator{
|
|
||||||
{addr1, sdk.NewInt(7)},
|
|
||||||
{addr2, sdk.NewInt(7)},
|
|
||||||
{addr3, sdk.NewInt(1)},
|
|
||||||
}}
|
|
||||||
|
|
||||||
key := sdk.NewKVStoreKey("testkey")
|
|
||||||
ctx := defaultContext(key)
|
|
||||||
|
|
||||||
bz, err := json.Marshal(valset)
|
|
||||||
require.Nil(t, err)
|
|
||||||
ctx = ctx.WithBlockHeader(abci.Header{ValidatorsHash: bz})
|
|
||||||
|
|
||||||
ork := NewKeeper(key, cdc, valset, sdk.NewDecWithPrec(667, 3), 100) // 66.7%
|
|
||||||
h := seqHandler(ork, key, sdk.CodespaceRoot)
|
|
||||||
|
|
||||||
// Nonmock.Validator signed, transaction failed
|
|
||||||
msg := Msg{seqOracle{0, 0}, []byte("randomguy")}
|
|
||||||
res := h(ctx, msg)
|
|
||||||
require.False(t, res.IsOK())
|
|
||||||
require.Equal(t, 0, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// Less than 2/3 signed, msg not processed
|
|
||||||
msg.Signer = addr1
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.True(t, res.IsOK())
|
|
||||||
require.Equal(t, 0, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// Double signed, transaction failed
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.False(t, res.IsOK())
|
|
||||||
require.Equal(t, 0, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// More than 2/3 signed, msg processed
|
|
||||||
msg.Signer = addr2
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.True(t, res.IsOK())
|
|
||||||
require.Equal(t, 1, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// Already processed, transaction failed
|
|
||||||
msg.Signer = addr3
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.False(t, res.IsOK())
|
|
||||||
require.Equal(t, 1, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// Less than 2/3 signed, msg not processed
|
|
||||||
msg = Msg{seqOracle{100, 1}, addr1}
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.True(t, res.IsOK())
|
|
||||||
require.Equal(t, 1, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// More than 2/3 signed but payload is invalid
|
|
||||||
msg.Signer = addr2
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.True(t, res.IsOK())
|
|
||||||
require.NotEqual(t, "", res.Log)
|
|
||||||
require.Equal(t, 1, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// Already processed, transaction failed
|
|
||||||
msg.Signer = addr3
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.False(t, res.IsOK())
|
|
||||||
require.Equal(t, 1, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// Should handle mock.Validator set change
|
|
||||||
valset.AddValidator(mock.Validator{addr4, sdk.NewInt(12)})
|
|
||||||
bz, err = json.Marshal(valset)
|
|
||||||
require.Nil(t, err)
|
|
||||||
ctx = ctx.WithBlockHeader(abci.Header{ValidatorsHash: bz})
|
|
||||||
|
|
||||||
// Less than 2/3 signed, msg not processed
|
|
||||||
msg = Msg{seqOracle{1, 2}, addr1}
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.True(t, res.IsOK())
|
|
||||||
require.Equal(t, 1, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// Less than 2/3 signed, msg not processed
|
|
||||||
msg.Signer = addr2
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.True(t, res.IsOK())
|
|
||||||
require.Equal(t, 1, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// More than 2/3 signed, msg processed
|
|
||||||
msg.Signer = addr4
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.True(t, res.IsOK())
|
|
||||||
require.Equal(t, 2, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// Should handle mock.Validator set change while oracle process is happening
|
|
||||||
msg = Msg{seqOracle{2, 3}, addr4}
|
|
||||||
|
|
||||||
// Less than 2/3 signed, msg not processed
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.True(t, res.IsOK())
|
|
||||||
require.Equal(t, 2, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// Signed mock.Validator is kicked out
|
|
||||||
valset.RemoveValidator(addr4)
|
|
||||||
bz, err = json.Marshal(valset)
|
|
||||||
require.Nil(t, err)
|
|
||||||
ctx = ctx.WithBlockHeader(abci.Header{ValidatorsHash: bz})
|
|
||||||
|
|
||||||
// Less than 2/3 signed, msg not processed
|
|
||||||
msg.Signer = addr1
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.True(t, res.IsOK())
|
|
||||||
require.Equal(t, 2, getSequence(ctx, key))
|
|
||||||
|
|
||||||
// More than 2/3 signed, msg processed
|
|
||||||
msg.Signer = addr2
|
|
||||||
res = h(ctx, msg)
|
|
||||||
require.True(t, res.IsOK())
|
|
||||||
require.Equal(t, 3, getSequence(ctx, key))
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package oracle
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Msg - struct for voting on payloads
|
|
||||||
type Msg struct {
|
|
||||||
Payload
|
|
||||||
Signer sdk.AccAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSignBytes implements sdk.Msg
|
|
||||||
func (msg Msg) GetSignBytes() []byte {
|
|
||||||
bz, err := json.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return sdk.MustSortJSON(bz)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSigners implements sdk.Msg
|
|
||||||
func (msg Msg) GetSigners() []sdk.AccAddress {
|
|
||||||
return []sdk.AccAddress{msg.Signer}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Payload defines inner data for actual execution
|
|
||||||
type Payload interface {
|
|
||||||
Route() string
|
|
||||||
Type() string
|
|
||||||
ValidateBasic() sdk.Error
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
package pow
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
priv1 = ed25519.GenPrivKey()
|
|
||||||
addr1 = sdk.AccAddress(priv1.PubKey().Address())
|
|
||||||
)
|
|
||||||
|
|
||||||
// initialize the mock application for this module
|
|
||||||
func getMockApp(t *testing.T) *mock.App {
|
|
||||||
mapp := mock.NewApp()
|
|
||||||
|
|
||||||
RegisterCodec(mapp.Cdc)
|
|
||||||
keyPOW := sdk.NewKVStoreKey("pow")
|
|
||||||
bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper)
|
|
||||||
config := Config{"pow", 1}
|
|
||||||
keeper := NewKeeper(keyPOW, config, bankKeeper, DefaultCodespace)
|
|
||||||
mapp.Router().AddRoute("pow", keeper.Handler)
|
|
||||||
|
|
||||||
mapp.SetInitChainer(getInitChainer(mapp, keeper))
|
|
||||||
|
|
||||||
require.NoError(t, mapp.CompleteSetup(keyPOW))
|
|
||||||
|
|
||||||
mapp.Seal()
|
|
||||||
|
|
||||||
return mapp
|
|
||||||
}
|
|
||||||
|
|
||||||
// overwrite the mock init chainer
|
|
||||||
func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer {
|
|
||||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
|
||||||
mapp.InitChainer(ctx, req)
|
|
||||||
|
|
||||||
genesis := Genesis{
|
|
||||||
Difficulty: 1,
|
|
||||||
Count: 0,
|
|
||||||
}
|
|
||||||
InitGenesis(ctx, keeper, genesis)
|
|
||||||
|
|
||||||
return abci.ResponseInitChain{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMsgMine(t *testing.T) {
|
|
||||||
mapp := getMockApp(t)
|
|
||||||
|
|
||||||
// Construct genesis state
|
|
||||||
acc1 := &auth.BaseAccount{
|
|
||||||
Address: addr1,
|
|
||||||
Coins: nil,
|
|
||||||
}
|
|
||||||
accs := []auth.Account{acc1}
|
|
||||||
|
|
||||||
// Initialize the chain (nil)
|
|
||||||
mock.SetGenesis(mapp, accs)
|
|
||||||
|
|
||||||
// A checkTx context (true)
|
|
||||||
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
|
|
||||||
res1 := mapp.AccountKeeper.GetAccount(ctxCheck, addr1)
|
|
||||||
require.Equal(t, acc1, res1)
|
|
||||||
|
|
||||||
// Mine and check for reward
|
|
||||||
mineMsg1 := GenerateMsgMine(addr1, 1, 2)
|
|
||||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []uint64{0}, []uint64{0}, true, true, priv1)
|
|
||||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(1))})
|
|
||||||
// Mine again and check for reward
|
|
||||||
mineMsg2 := GenerateMsgMine(addr1, 2, 3)
|
|
||||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []uint64{0}, []uint64{1}, true, true, priv1)
|
|
||||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(2))})
|
|
||||||
// Mine again - should be invalid
|
|
||||||
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []uint64{0}, []uint64{1}, false, false, priv1)
|
|
||||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(2))})
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/utils"
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/pow"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
// command to mine some pow!
|
|
||||||
func MineCmd(cdc *codec.Codec) *cobra.Command {
|
|
||||||
return &cobra.Command{
|
|
||||||
Use: "mine [difficulty] [count] [nonce] [solution]",
|
|
||||||
Short: "Mine some coins with proof-of-work!",
|
|
||||||
Args: cobra.ExactArgs(4),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
|
|
||||||
cliCtx := context.NewCLIContext().
|
|
||||||
WithCodec(cdc).
|
|
||||||
WithAccountDecoder(cdc)
|
|
||||||
|
|
||||||
from, err := cliCtx.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.NewMsgMine(from, difficulty, count, nonce, solution)
|
|
||||||
|
|
||||||
// Build and sign the transaction, then broadcast to a Tendermint
|
|
||||||
// node.
|
|
||||||
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package pow
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Register concrete types on codec codec
|
|
||||||
func RegisterCodec(cdc *codec.Codec) {
|
|
||||||
cdc.RegisterConcrete(MsgMine{}, "pow/Mine", nil)
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
package pow
|
|
||||||
|
|
||||||
import (
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO remove, seems hacky
|
|
||||||
type CodeType = sdk.CodeType
|
|
||||||
|
|
||||||
// POW errors reserve 200 ~ 299
|
|
||||||
const (
|
|
||||||
DefaultCodespace sdk.CodespaceType = "pow"
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint
|
|
||||||
func ErrInvalidDifficulty(codespace sdk.CodespaceType, msg string) sdk.Error {
|
|
||||||
return newError(codespace, CodeInvalidDifficulty, msg)
|
|
||||||
}
|
|
||||||
func ErrNonexistentDifficulty(codespace sdk.CodespaceType) sdk.Error {
|
|
||||||
return newError(codespace, CodeNonexistentDifficulty, "")
|
|
||||||
}
|
|
||||||
func ErrNonexistentReward(codespace sdk.CodespaceType) sdk.Error {
|
|
||||||
return newError(codespace, CodeNonexistentReward, "")
|
|
||||||
}
|
|
||||||
func ErrNonexistentCount(codespace sdk.CodespaceType) sdk.Error {
|
|
||||||
return newError(codespace, CodeNonexistentCount, "")
|
|
||||||
}
|
|
||||||
func ErrInvalidProof(codespace sdk.CodespaceType, msg string) sdk.Error {
|
|
||||||
return newError(codespace, CodeInvalidProof, msg)
|
|
||||||
}
|
|
||||||
func ErrNotBelowTarget(codespace sdk.CodespaceType, msg string) sdk.Error {
|
|
||||||
return newError(codespace, CodeNotBelowTarget, msg)
|
|
||||||
}
|
|
||||||
func ErrInvalidCount(codespace sdk.CodespaceType, msg string) sdk.Error {
|
|
||||||
return newError(codespace, CodeInvalidCount, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func msgOrDefaultMsg(msg string, code CodeType) string {
|
|
||||||
if msg != "" {
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
return codeToDefaultMsg(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newError(codespace sdk.CodespaceType, code CodeType, msg string) sdk.Error {
|
|
||||||
msg = msgOrDefaultMsg(msg, code)
|
|
||||||
return sdk.NewError(codespace, code, msg)
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package pow
|
|
||||||
|
|
||||||
import (
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// POW handler
|
|
||||||
func (pk Keeper) Handler(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
|
||||||
switch msg := msg.(type) {
|
|
||||||
case MsgMine:
|
|
||||||
return handleMsgMine(ctx, pk, msg)
|
|
||||||
default:
|
|
||||||
errMsg := "Unrecognized pow Msg type: " + msg.Type()
|
|
||||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgMine(ctx sdk.Context, pk Keeper, msg MsgMine) sdk.Result {
|
|
||||||
|
|
||||||
// precondition: msg has passed ValidateBasic
|
|
||||||
|
|
||||||
newDiff, newCount, err := pk.CheckValid(ctx, msg.Difficulty, msg.Count)
|
|
||||||
if err != nil {
|
|
||||||
return err.Result()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pk.ApplyValid(ctx, msg.Sender, newDiff, newCount)
|
|
||||||
if err != nil {
|
|
||||||
return err.Result()
|
|
||||||
}
|
|
||||||
|
|
||||||
return sdk.Result{}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package pow
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPowHandler(t *testing.T) {
|
|
||||||
input := setupTestInput()
|
|
||||||
ctx := input.ctx
|
|
||||||
|
|
||||||
config := NewConfig("pow", int64(1))
|
|
||||||
keeper := NewKeeper(input.capKey, config, input.bk, DefaultCodespace)
|
|
||||||
|
|
||||||
handler := keeper.Handler
|
|
||||||
|
|
||||||
addr := sdk.AccAddress([]byte("sender"))
|
|
||||||
count := uint64(1)
|
|
||||||
difficulty := uint64(2)
|
|
||||||
|
|
||||||
err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)})
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
nonce, proof := mine(addr, count, difficulty)
|
|
||||||
msg := NewMsgMine(addr, difficulty, count, nonce, proof)
|
|
||||||
|
|
||||||
result := handler(ctx, msg)
|
|
||||||
require.Equal(t, result, sdk.Result{})
|
|
||||||
|
|
||||||
newDiff, err := keeper.GetLastDifficulty(ctx)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Equal(t, newDiff, uint64(2))
|
|
||||||
|
|
||||||
newCount, err := keeper.GetLastCount(ctx)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.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 = NewMsgMine(addr, difficulty, count, nonce, proof)
|
|
||||||
|
|
||||||
result = handler(ctx, msg)
|
|
||||||
require.NotEqual(t, result, sdk.Result{})
|
|
||||||
}
|
|
|
@ -1,135 +0,0 @@
|
||||||
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 Config struct {
|
|
||||||
Denomination string
|
|
||||||
Reward int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// genesis info must specify starting difficulty and starting count
|
|
||||||
type Genesis struct {
|
|
||||||
Difficulty uint64 `json:"difficulty"`
|
|
||||||
Count uint64 `json:"count"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// POW Keeper
|
|
||||||
type Keeper struct {
|
|
||||||
key sdk.StoreKey
|
|
||||||
config Config
|
|
||||||
ck bank.Keeper
|
|
||||||
codespace sdk.CodespaceType
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConfig(denomination string, reward int64) Config {
|
|
||||||
return Config{denomination, reward}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewKeeper(key sdk.StoreKey, config Config, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
|
||||||
return Keeper{key, config, ck, codespace}
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitGenesis for the POW module
|
|
||||||
func InitGenesis(ctx sdk.Context, k Keeper, genesis Genesis) error {
|
|
||||||
k.SetLastDifficulty(ctx, genesis.Difficulty)
|
|
||||||
k.SetLastCount(ctx, genesis.Count)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExportGenesis for the PoW module
|
|
||||||
func ExportGenesis(ctx sdk.Context, k Keeper) Genesis {
|
|
||||||
difficulty, err := k.GetLastDifficulty(ctx)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
count, err := k.GetLastCount(ctx)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return Genesis{
|
|
||||||
difficulty,
|
|
||||||
count,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastDifficultyKey = []byte("lastDifficultyKey")
|
|
||||||
|
|
||||||
// get the last mining difficulty
|
|
||||||
func (k Keeper) GetLastDifficulty(ctx sdk.Context) (uint64, error) {
|
|
||||||
store := ctx.KVStore(k.key)
|
|
||||||
stored := store.Get(lastDifficultyKey)
|
|
||||||
if stored == nil {
|
|
||||||
panic("no stored difficulty")
|
|
||||||
} else {
|
|
||||||
return strconv.ParseUint(string(stored), 0, 64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the last mining difficulty
|
|
||||||
func (k Keeper) SetLastDifficulty(ctx sdk.Context, diff uint64) {
|
|
||||||
store := ctx.KVStore(k.key)
|
|
||||||
store.Set(lastDifficultyKey, []byte(strconv.FormatUint(diff, 16)))
|
|
||||||
}
|
|
||||||
|
|
||||||
var countKey = []byte("count")
|
|
||||||
|
|
||||||
// get the last count
|
|
||||||
func (k Keeper) GetLastCount(ctx sdk.Context) (uint64, error) {
|
|
||||||
store := ctx.KVStore(k.key)
|
|
||||||
stored := store.Get(countKey)
|
|
||||||
if stored == nil {
|
|
||||||
panic("no stored count")
|
|
||||||
} else {
|
|
||||||
return strconv.ParseUint(string(stored), 0, 64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the last count
|
|
||||||
func (k Keeper) SetLastCount(ctx sdk.Context, count uint64) {
|
|
||||||
store := ctx.KVStore(k.key)
|
|
||||||
store.Set(countKey, []byte(strconv.FormatUint(count, 16)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is the keeper state valid?
|
|
||||||
func (k Keeper) CheckValid(ctx sdk.Context, difficulty uint64, count uint64) (uint64, uint64, sdk.Error) {
|
|
||||||
|
|
||||||
lastDifficulty, err := k.GetLastDifficulty(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, ErrNonexistentDifficulty(k.codespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
newDifficulty := lastDifficulty + 1
|
|
||||||
|
|
||||||
lastCount, err := k.GetLastCount(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, ErrNonexistentCount(k.codespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
newCount := lastCount + 1
|
|
||||||
|
|
||||||
if count != newCount {
|
|
||||||
return 0, 0, ErrInvalidCount(k.codespace, fmt.Sprintf("invalid count: was %d, should have been %d", count, newCount))
|
|
||||||
}
|
|
||||||
if difficulty != newDifficulty {
|
|
||||||
return 0, 0, ErrInvalidDifficulty(k.codespace, fmt.Sprintf("invalid difficulty: was %d, should have been %d", difficulty, newDifficulty))
|
|
||||||
}
|
|
||||||
return newDifficulty, newCount, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add some coins for a POW well done
|
|
||||||
func (k Keeper) ApplyValid(ctx sdk.Context, sender sdk.AccAddress, newDifficulty uint64, newCount uint64) sdk.Error {
|
|
||||||
_, _, ckErr := k.ck.AddCoins(ctx, sender, []sdk.Coin{sdk.NewInt64Coin(k.config.Denomination, k.config.Reward)})
|
|
||||||
if ckErr != nil {
|
|
||||||
return ckErr
|
|
||||||
}
|
|
||||||
k.SetLastDifficulty(ctx, newDifficulty)
|
|
||||||
k.SetLastCount(ctx, newCount)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
package pow
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"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"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testInput struct {
|
|
||||||
cdc *codec.Codec
|
|
||||||
ctx sdk.Context
|
|
||||||
capKey *sdk.KVStoreKey
|
|
||||||
bk bank.BaseKeeper
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupTestInput() testInput {
|
|
||||||
db := dbm.NewMemDB()
|
|
||||||
|
|
||||||
cdc := codec.New()
|
|
||||||
auth.RegisterBaseAccount(cdc)
|
|
||||||
|
|
||||||
capKey := sdk.NewKVStoreKey("capkey")
|
|
||||||
keyParams := sdk.NewKVStoreKey("params")
|
|
||||||
tkeyParams := sdk.NewTransientStoreKey("transient_params")
|
|
||||||
|
|
||||||
ms := store.NewCommitMultiStore(db)
|
|
||||||
ms.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db)
|
|
||||||
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
|
|
||||||
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
|
|
||||||
ms.LoadLatestVersion()
|
|
||||||
|
|
||||||
pk := params.NewKeeper(cdc, keyParams, tkeyParams)
|
|
||||||
ak := auth.NewAccountKeeper(cdc, capKey, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount)
|
|
||||||
bk := bank.NewBaseKeeper(ak)
|
|
||||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
|
||||||
|
|
||||||
return testInput{cdc: cdc, ctx: ctx, capKey: capKey, bk: bk}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPowKeeperGetSet(t *testing.T) {
|
|
||||||
input := setupTestInput()
|
|
||||||
ctx := input.ctx
|
|
||||||
|
|
||||||
config := NewConfig("pow", int64(1))
|
|
||||||
keeper := NewKeeper(input.capKey, config, input.bk, DefaultCodespace)
|
|
||||||
|
|
||||||
err := InitGenesis(ctx, keeper, Genesis{uint64(1), uint64(0)})
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
genesis := ExportGenesis(ctx, keeper)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Equal(t, genesis, Genesis{uint64(1), uint64(0)})
|
|
||||||
|
|
||||||
res, err := keeper.GetLastDifficulty(ctx)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Equal(t, res, uint64(1))
|
|
||||||
|
|
||||||
keeper.SetLastDifficulty(ctx, 2)
|
|
||||||
|
|
||||||
res, err = keeper.GetLastDifficulty(ctx)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Equal(t, res, uint64(2))
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
package pow
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// generate the mine message
|
|
||||||
func GenerateMsgMine(sender sdk.AccAddress, count uint64, difficulty uint64) MsgMine {
|
|
||||||
nonce, hash := mine(sender, count, difficulty)
|
|
||||||
return NewMsgMine(sender, difficulty, count, nonce, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hash(sender sdk.AccAddress, 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.AccAddress, 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
package pow
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MsgMine - mine some coins with PoW
|
|
||||||
type MsgMine struct {
|
|
||||||
Sender sdk.AccAddress `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 = MsgMine{}
|
|
||||||
|
|
||||||
// NewMsgMine - construct mine message
|
|
||||||
func NewMsgMine(sender sdk.AccAddress, difficulty uint64, count uint64, nonce uint64, proof []byte) MsgMine {
|
|
||||||
return MsgMine{sender, difficulty, count, nonce, proof}
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint
|
|
||||||
func (msg MsgMine) Route() string { return "pow" }
|
|
||||||
func (msg MsgMine) Type() string { return "mine" }
|
|
||||||
func (msg MsgMine) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Sender} }
|
|
||||||
func (msg MsgMine) String() string {
|
|
||||||
return fmt.Sprintf("MsgMine{Sender: %s, Difficulty: %d, Count: %d, Nonce: %d, Proof: %s}", msg.Sender, msg.Difficulty, msg.Count, msg.Nonce, msg.Proof)
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate the mine message
|
|
||||||
func (msg MsgMine) ValidateBasic() sdk.Error {
|
|
||||||
// check hash
|
|
||||||
var data []byte
|
|
||||||
// hash must include sender, so no other users can race the tx
|
|
||||||
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(DefaultCodespace, 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(DefaultCodespace, fmt.Sprintf("proof: %s", msg.Proof))
|
|
||||||
}
|
|
||||||
if hashUint >= target {
|
|
||||||
return ErrNotBelowTarget(DefaultCodespace, fmt.Sprintf("hashuint: %d, target: %d", hashUint, target))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the mine message sign bytes
|
|
||||||
func (msg MsgMine) GetSignBytes() []byte {
|
|
||||||
b, err := json.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return sdk.MustSortJSON(b)
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
package pow
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewMsgMine(t *testing.T) {
|
|
||||||
addr := sdk.AccAddress([]byte("sender"))
|
|
||||||
msg := MsgMine{addr, 0, 0, 0, []byte("")}
|
|
||||||
equiv := NewMsgMine(addr, 0, 0, 0, []byte(""))
|
|
||||||
require.Equal(t, msg, equiv, "%s != %s", msg, equiv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMsgMineType(t *testing.T) {
|
|
||||||
addr := sdk.AccAddress([]byte("sender"))
|
|
||||||
msg := MsgMine{addr, 0, 0, 0, []byte("")}
|
|
||||||
require.Equal(t, msg.Route(), "pow")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMsgMineValidation(t *testing.T) {
|
|
||||||
addr := sdk.AccAddress([]byte("sender"))
|
|
||||||
otherAddr := sdk.AccAddress([]byte("another"))
|
|
||||||
count := uint64(0)
|
|
||||||
|
|
||||||
for difficulty := uint64(1); difficulty < 1000; difficulty += 100 {
|
|
||||||
|
|
||||||
count++
|
|
||||||
nonce, proof := mine(addr, count, difficulty)
|
|
||||||
msg := MsgMine{addr, difficulty, count, nonce, proof}
|
|
||||||
err := msg.ValidateBasic()
|
|
||||||
require.Nil(t, err, "error with difficulty %d - %+v", difficulty, err)
|
|
||||||
|
|
||||||
msg.Count++
|
|
||||||
err = msg.ValidateBasic()
|
|
||||||
require.NotNil(t, err, "count was wrong, should have thrown error with msg %s", msg)
|
|
||||||
|
|
||||||
msg.Count--
|
|
||||||
msg.Nonce++
|
|
||||||
err = msg.ValidateBasic()
|
|
||||||
require.NotNil(t, err, "nonce was wrong, should have thrown error with msg %s", msg)
|
|
||||||
|
|
||||||
msg.Nonce--
|
|
||||||
msg.Sender = otherAddr
|
|
||||||
err = msg.ValidateBasic()
|
|
||||||
require.NotNil(t, err, "sender was wrong, should have thrown error with msg %s", msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMsgMineString(t *testing.T) {
|
|
||||||
addr := sdk.AccAddress([]byte("sender"))
|
|
||||||
msg := MsgMine{addr, 0, 0, 0, []byte("abc")}
|
|
||||||
res := msg.String()
|
|
||||||
require.Equal(t, res, "MsgMine{Sender: cosmos1wdjkuer9wgh76ts6, Difficulty: 0, Count: 0, Nonce: 0, Proof: abc}")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMsgMineGetSignBytes(t *testing.T) {
|
|
||||||
addr := sdk.AccAddress([]byte("sender"))
|
|
||||||
msg := MsgMine{addr, 1, 1, 1, []byte("abc")}
|
|
||||||
res := msg.GetSignBytes()
|
|
||||||
require.Equal(t, string(res), `{"count":1,"difficulty":1,"nonce":1,"proof":"YWJj","sender":"cosmos1wdjkuer9wgh76ts6"}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMsgMineGetSigners(t *testing.T) {
|
|
||||||
addr := sdk.AccAddress([]byte("sender"))
|
|
||||||
msg := MsgMine{addr, 1, 1, 1, []byte("abc")}
|
|
||||||
res := msg.GetSigners()
|
|
||||||
require.Equal(t, fmt.Sprintf("%v", res), "[73656E646572]")
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/utils"
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/simplestaking"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
flagStake = "staking"
|
|
||||||
flagValidator = "validator"
|
|
||||||
)
|
|
||||||
|
|
||||||
// simple bond tx
|
|
||||||
func BondTxCmd(cdc *codec.Codec) *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "bond",
|
|
||||||
Short: "Bond to a validator",
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
|
|
||||||
cliCtx := context.NewCLIContext().
|
|
||||||
WithCodec(cdc).
|
|
||||||
WithAccountDecoder(cdc)
|
|
||||||
|
|
||||||
from, err := cliCtx.GetFromAddress()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
stakingString := viper.GetString(flagStake)
|
|
||||||
if len(stakingString) == 0 {
|
|
||||||
return fmt.Errorf("specify coins to bond with --staking")
|
|
||||||
}
|
|
||||||
|
|
||||||
valString := viper.GetString(flagValidator)
|
|
||||||
if len(valString) == 0 {
|
|
||||||
return fmt.Errorf("specify pubkey to bond to with --validator")
|
|
||||||
}
|
|
||||||
|
|
||||||
staking, err := sdk.ParseCoin(stakingString)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: bech32 ...
|
|
||||||
rawPubKey, err := hex.DecodeString(valString)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var pubKeyEd ed25519.PubKeyEd25519
|
|
||||||
copy(pubKeyEd[:], rawPubKey)
|
|
||||||
|
|
||||||
msg := simplestaking.NewMsgBond(from, staking, pubKeyEd)
|
|
||||||
|
|
||||||
// Build and sign the transaction, then broadcast to a Tendermint
|
|
||||||
// node.
|
|
||||||
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Flags().String(flagStake, "", "Amount of coins to stake")
|
|
||||||
cmd.Flags().String(flagValidator, "", "Validator address to stake")
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// simple unbond tx
|
|
||||||
func UnbondTxCmd(cdc *codec.Codec) *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "unbond",
|
|
||||||
Short: "Unbond from a validator",
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
|
|
||||||
cliCtx := context.NewCLIContext().
|
|
||||||
WithCodec(cdc)
|
|
||||||
|
|
||||||
from, err := cliCtx.GetFromAddress()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := simplestaking.NewMsgUnbond(from)
|
|
||||||
|
|
||||||
// Build and sign the transaction, then broadcast to a Tendermint
|
|
||||||
// node.
|
|
||||||
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package simplestaking
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Register concrete types on codec codec
|
|
||||||
func RegisterCodec(cdc *codec.Codec) {
|
|
||||||
cdc.RegisterConcrete(MsgBond{}, "simplestaking/BondMsg", nil)
|
|
||||||
cdc.RegisterConcrete(MsgUnbond{}, "simplestaking/UnbondMsg", nil)
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package simplestaking
|
|
||||||
|
|
||||||
import (
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// simple staking errors reserve 300 ~ 399.
|
|
||||||
const (
|
|
||||||
DefaultCodespace sdk.CodespaceType = moduleName
|
|
||||||
|
|
||||||
// simplestaking errors reserve 300 - 399.
|
|
||||||
CodeEmptyValidator sdk.CodeType = 300
|
|
||||||
CodeInvalidUnbond sdk.CodeType = 301
|
|
||||||
CodeEmptyStake sdk.CodeType = 302
|
|
||||||
CodeIncorrectStakingToken sdk.CodeType = 303
|
|
||||||
)
|
|
||||||
|
|
||||||
// nolint
|
|
||||||
func ErrIncorrectStakingToken(codespace sdk.CodespaceType) sdk.Error {
|
|
||||||
return newError(codespace, CodeIncorrectStakingToken, "")
|
|
||||||
}
|
|
||||||
func ErrEmptyValidator(codespace sdk.CodespaceType) sdk.Error {
|
|
||||||
return newError(codespace, CodeEmptyValidator, "")
|
|
||||||
}
|
|
||||||
func ErrInvalidUnbond(codespace sdk.CodespaceType) sdk.Error {
|
|
||||||
return newError(codespace, CodeInvalidUnbond, "")
|
|
||||||
}
|
|
||||||
func ErrEmptyStake(codespace sdk.CodespaceType) sdk.Error {
|
|
||||||
return newError(codespace, CodeEmptyStake, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------
|
|
||||||
// Helpers
|
|
||||||
|
|
||||||
// nolint: unparam
|
|
||||||
func newError(codespace sdk.CodespaceType, code sdk.CodeType, msg string) sdk.Error {
|
|
||||||
return sdk.NewError(codespace, code, msg)
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package simplestaking
|
|
||||||
|
|
||||||
import (
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewHandler returns a handler for "simplestaking" type messages.
|
|
||||||
func NewHandler(k Keeper) sdk.Handler {
|
|
||||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
|
||||||
switch msg.(type) {
|
|
||||||
case MsgBond:
|
|
||||||
return handleMsgBond()
|
|
||||||
case MsgUnbond:
|
|
||||||
return handleMsgUnbond()
|
|
||||||
default:
|
|
||||||
return sdk.ErrUnknownRequest("No match for message type.").Result()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgBond() sdk.Result {
|
|
||||||
// Removed ValidatorSet from result because it does not get used.
|
|
||||||
// TODO: Implement correct bond/unbond handling
|
|
||||||
return sdk.Result{
|
|
||||||
Code: sdk.CodeOK,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgUnbond() sdk.Result {
|
|
||||||
return sdk.Result{
|
|
||||||
Code: sdk.CodeOK,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
package simplestaking
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tendermint/tendermint/crypto"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
|
||||||
stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
const moduleName = "simplestaking"
|
|
||||||
|
|
||||||
// simple staking keeper
|
|
||||||
type Keeper struct {
|
|
||||||
ck bank.Keeper
|
|
||||||
|
|
||||||
key sdk.StoreKey
|
|
||||||
cdc *codec.Codec
|
|
||||||
codespace sdk.CodespaceType
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewKeeper(key sdk.StoreKey, bankKeeper bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
|
||||||
cdc := codec.New()
|
|
||||||
codec.RegisterCrypto(cdc)
|
|
||||||
return Keeper{
|
|
||||||
key: key,
|
|
||||||
cdc: cdc,
|
|
||||||
ck: bankKeeper,
|
|
||||||
codespace: codespace,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keeper) getBondInfo(ctx sdk.Context, addr sdk.AccAddress) bondInfo {
|
|
||||||
store := ctx.KVStore(k.key)
|
|
||||||
bz := store.Get(addr)
|
|
||||||
if bz == nil {
|
|
||||||
return bondInfo{}
|
|
||||||
}
|
|
||||||
var bi bondInfo
|
|
||||||
err := k.cdc.UnmarshalBinaryLengthPrefixed(bz, &bi)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return bi
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keeper) setBondInfo(ctx sdk.Context, addr sdk.AccAddress, bi bondInfo) {
|
|
||||||
store := ctx.KVStore(k.key)
|
|
||||||
bz, err := k.cdc.MarshalBinaryLengthPrefixed(bi)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
store.Set(addr, bz)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keeper) deleteBondInfo(ctx sdk.Context, addr sdk.AccAddress) {
|
|
||||||
store := ctx.KVStore(k.key)
|
|
||||||
store.Delete(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// register a bond with the keeper
|
|
||||||
func (k Keeper) Bond(ctx sdk.Context, addr sdk.AccAddress, pubKey crypto.PubKey, staking sdk.Coin) (int64, sdk.Error) {
|
|
||||||
if staking.Denom != stakingTypes.DefaultBondDenom {
|
|
||||||
return 0, ErrIncorrectStakingToken(k.codespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err := k.ck.SubtractCoins(ctx, addr, []sdk.Coin{staking})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
bi := k.getBondInfo(ctx, addr)
|
|
||||||
if bi.isEmpty() {
|
|
||||||
bi = bondInfo{
|
|
||||||
PubKey: pubKey,
|
|
||||||
Power: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bi.Power = bi.Power + staking.Amount.Int64()
|
|
||||||
|
|
||||||
k.setBondInfo(ctx, addr, bi)
|
|
||||||
return bi.Power, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// register an unbond with the keeper
|
|
||||||
func (k Keeper) Unbond(ctx sdk.Context, addr sdk.AccAddress) (crypto.PubKey, int64, sdk.Error) {
|
|
||||||
bi := k.getBondInfo(ctx, addr)
|
|
||||||
if bi.isEmpty() {
|
|
||||||
return nil, 0, ErrInvalidUnbond(k.codespace)
|
|
||||||
}
|
|
||||||
k.deleteBondInfo(ctx, addr)
|
|
||||||
|
|
||||||
returnedBond := sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, bi.Power)
|
|
||||||
|
|
||||||
_, _, err := k.ck.AddCoins(ctx, addr, []sdk.Coin{returnedBond})
|
|
||||||
if err != nil {
|
|
||||||
return bi.PubKey, bi.Power, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bi.PubKey, bi.Power, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FOR TESTING PURPOSES -------------------------------------------------
|
|
||||||
|
|
||||||
func (k Keeper) bondWithoutCoins(ctx sdk.Context, addr sdk.AccAddress, pubKey crypto.PubKey, staking sdk.Coin) (int64, sdk.Error) {
|
|
||||||
if staking.Denom != stakingTypes.DefaultBondDenom {
|
|
||||||
return 0, ErrIncorrectStakingToken(k.codespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
bi := k.getBondInfo(ctx, addr)
|
|
||||||
if bi.isEmpty() {
|
|
||||||
bi = bondInfo{
|
|
||||||
PubKey: pubKey,
|
|
||||||
Power: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bi.Power = bi.Power + staking.Amount.Int64()
|
|
||||||
|
|
||||||
k.setBondInfo(ctx, addr, bi)
|
|
||||||
return bi.Power, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Keeper) unbondWithoutCoins(ctx sdk.Context, addr sdk.AccAddress) (crypto.PubKey, int64, sdk.Error) {
|
|
||||||
bi := k.getBondInfo(ctx, addr)
|
|
||||||
if bi.isEmpty() {
|
|
||||||
return nil, 0, ErrInvalidUnbond(k.codespace)
|
|
||||||
}
|
|
||||||
k.deleteBondInfo(ctx, addr)
|
|
||||||
|
|
||||||
return bi.PubKey, bi.Power, nil
|
|
||||||
}
|
|
|
@ -1,104 +0,0 @@
|
||||||
package simplestaking
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/params"
|
|
||||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testInput struct {
|
|
||||||
cdc *codec.Codec
|
|
||||||
ctx sdk.Context
|
|
||||||
capKey *sdk.KVStoreKey
|
|
||||||
bk bank.BaseKeeper
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupTestInput() testInput {
|
|
||||||
db := dbm.NewMemDB()
|
|
||||||
|
|
||||||
cdc := codec.New()
|
|
||||||
auth.RegisterBaseAccount(cdc)
|
|
||||||
|
|
||||||
capKey := sdk.NewKVStoreKey("capkey")
|
|
||||||
keyParams := sdk.NewKVStoreKey("params")
|
|
||||||
tkeyParams := sdk.NewTransientStoreKey("transient_params")
|
|
||||||
|
|
||||||
ms := store.NewCommitMultiStore(db)
|
|
||||||
ms.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db)
|
|
||||||
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
|
|
||||||
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
|
|
||||||
ms.LoadLatestVersion()
|
|
||||||
|
|
||||||
pk := params.NewKeeper(cdc, keyParams, tkeyParams)
|
|
||||||
ak := auth.NewAccountKeeper(cdc, capKey, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount)
|
|
||||||
bk := bank.NewBaseKeeper(ak)
|
|
||||||
ctx := sdk.NewContext(ms, abci.Header{}, false, nil)
|
|
||||||
|
|
||||||
return testInput{cdc: cdc, ctx: ctx, capKey: capKey, bk: bk}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeeperGetSet(t *testing.T) {
|
|
||||||
input := setupTestInput()
|
|
||||||
ctx := input.ctx
|
|
||||||
|
|
||||||
stakingKeeper := NewKeeper(input.capKey, input.bk, DefaultCodespace)
|
|
||||||
addr := sdk.AccAddress([]byte("some-address"))
|
|
||||||
|
|
||||||
bi := stakingKeeper.getBondInfo(ctx, addr)
|
|
||||||
require.Equal(t, bi, bondInfo{})
|
|
||||||
|
|
||||||
privKey := ed25519.GenPrivKey()
|
|
||||||
|
|
||||||
bi = bondInfo{
|
|
||||||
PubKey: privKey.PubKey(),
|
|
||||||
Power: int64(10),
|
|
||||||
}
|
|
||||||
fmt.Printf("Pubkey: %v\n", privKey.PubKey())
|
|
||||||
stakingKeeper.setBondInfo(ctx, addr, bi)
|
|
||||||
|
|
||||||
savedBi := stakingKeeper.getBondInfo(ctx, addr)
|
|
||||||
require.NotNil(t, savedBi)
|
|
||||||
fmt.Printf("Bond Info: %v\n", savedBi)
|
|
||||||
require.Equal(t, int64(10), savedBi.Power)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBonding(t *testing.T) {
|
|
||||||
input := setupTestInput()
|
|
||||||
ctx := input.ctx
|
|
||||||
|
|
||||||
stakingKeeper := NewKeeper(input.capKey, input.bk, DefaultCodespace)
|
|
||||||
addr := sdk.AccAddress([]byte("some-address"))
|
|
||||||
privKey := ed25519.GenPrivKey()
|
|
||||||
pubKey := privKey.PubKey()
|
|
||||||
|
|
||||||
_, _, err := stakingKeeper.unbondWithoutCoins(ctx, addr)
|
|
||||||
require.Equal(t, err, ErrInvalidUnbond(DefaultCodespace))
|
|
||||||
|
|
||||||
_, err = stakingKeeper.bondWithoutCoins(ctx, addr, pubKey, sdk.NewInt64Coin(stakingtypes.DefaultBondDenom, 10))
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
power, err := stakingKeeper.bondWithoutCoins(ctx, addr, pubKey, sdk.NewInt64Coin(stakingtypes.DefaultBondDenom, 10))
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Equal(t, int64(20), power)
|
|
||||||
|
|
||||||
pk, _, err := stakingKeeper.unbondWithoutCoins(ctx, addr)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Equal(t, pubKey, pk)
|
|
||||||
|
|
||||||
_, _, err = stakingKeeper.unbondWithoutCoins(ctx, addr)
|
|
||||||
require.Equal(t, err, ErrInvalidUnbond(DefaultCodespace))
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
package simplestaking
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
//_________________________________________________________----
|
|
||||||
|
|
||||||
// simple bond message
|
|
||||||
type MsgBond struct {
|
|
||||||
Address sdk.AccAddress `json:"address"`
|
|
||||||
Stake sdk.Coin `json:"coins"`
|
|
||||||
PubKey crypto.PubKey `json:"pub_key"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMsgBond(addr sdk.AccAddress, stake sdk.Coin, pubKey crypto.PubKey) MsgBond {
|
|
||||||
return MsgBond{
|
|
||||||
Address: addr,
|
|
||||||
Stake: stake,
|
|
||||||
PubKey: pubKey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint
|
|
||||||
func (msg MsgBond) Route() string { return moduleName } //TODO update "staking/createvalidator"
|
|
||||||
func (msg MsgBond) Type() string { return "bond" }
|
|
||||||
func (msg MsgBond) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Address} }
|
|
||||||
|
|
||||||
// basic validation of the bond message
|
|
||||||
func (msg MsgBond) ValidateBasic() sdk.Error {
|
|
||||||
if msg.Stake.IsZero() {
|
|
||||||
return ErrEmptyStake(DefaultCodespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.PubKey == nil {
|
|
||||||
return sdk.ErrInvalidPubKey("MsgBond.PubKey must not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// get bond message sign bytes
|
|
||||||
func (msg MsgBond) GetSignBytes() []byte {
|
|
||||||
bz, err := json.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return sdk.MustSortJSON(bz)
|
|
||||||
}
|
|
||||||
|
|
||||||
//_______________________________________________________________
|
|
||||||
|
|
||||||
// simple unbond message
|
|
||||||
type MsgUnbond struct {
|
|
||||||
Address sdk.AccAddress `json:"address"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMsgUnbond(addr sdk.AccAddress) MsgUnbond {
|
|
||||||
return MsgUnbond{
|
|
||||||
Address: addr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint
|
|
||||||
func (msg MsgUnbond) Route() string { return moduleName } //TODO update "staking/createvalidator"
|
|
||||||
func (msg MsgUnbond) Type() string { return "unbond" }
|
|
||||||
func (msg MsgUnbond) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Address} }
|
|
||||||
func (msg MsgUnbond) ValidateBasic() sdk.Error { return nil }
|
|
||||||
|
|
||||||
// get unbond message sign bytes
|
|
||||||
func (msg MsgUnbond) GetSignBytes() []byte {
|
|
||||||
bz, err := json.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return bz
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package simplestaking
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBondMsgValidation(t *testing.T) {
|
|
||||||
privKey := ed25519.GenPrivKey()
|
|
||||||
cases := []struct {
|
|
||||||
valid bool
|
|
||||||
msgBond MsgBond
|
|
||||||
}{
|
|
||||||
{true, NewMsgBond(sdk.AccAddress{}, sdk.NewInt64Coin("mycoin", 5), privKey.PubKey())},
|
|
||||||
{false, NewMsgBond(sdk.AccAddress{}, sdk.NewInt64Coin("mycoin", 0), privKey.PubKey())},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range cases {
|
|
||||||
err := tc.msgBond.ValidateBasic()
|
|
||||||
if tc.valid {
|
|
||||||
require.Nil(t, err, "%d: %+v", i, err)
|
|
||||||
} else {
|
|
||||||
require.NotNil(t, err, "%d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package simplestaking
|
|
||||||
|
|
||||||
import "github.com/tendermint/tendermint/crypto"
|
|
||||||
|
|
||||||
type bondInfo struct {
|
|
||||||
PubKey crypto.PubKey
|
|
||||||
Power int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bi bondInfo) isEmpty() bool {
|
|
||||||
if bi == (bondInfo{}) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package sketchy
|
|
||||||
|
|
||||||
import (
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is just an example to demonstrate a "sketchy" third-party handler module,
|
|
||||||
to demonstrate the "object capability" model for security.
|
|
||||||
|
|
||||||
Since nothing is passed in via arguments to the NewHandler constructor,
|
|
||||||
it cannot affect the handling of other transaction types.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Handle all "sketchy" type messages.
|
|
||||||
func NewHandler() sdk.Handler {
|
|
||||||
|
|
||||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
|
||||||
// There's nothing accessible from ctx or msg (even using reflection)
|
|
||||||
// that can mutate the state of the application.
|
|
||||||
return sdk.Result{}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Binary file not shown.
|
@ -1,91 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/abci/server"
|
|
||||||
"github.com/tendermint/tendermint/libs/cli"
|
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
|
|
||||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", bam.MainStoreKey)
|
|
||||||
|
|
||||||
rootDir := viper.GetString(cli.HomeFlag)
|
|
||||||
db, err := dbm.NewGoLevelDB("basecoind", filepath.Join(rootDir, "data"))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capabilities key to access the main KVStore.
|
|
||||||
var capKeyMainStore = sdk.NewKVStoreKey(bam.MainStoreKey)
|
|
||||||
|
|
||||||
// Create BaseApp.
|
|
||||||
var baseApp = bam.NewBaseApp("kvstore", logger, db, decodeTx)
|
|
||||||
|
|
||||||
// Set mounts for BaseApp's MultiStore.
|
|
||||||
baseApp.MountStores(capKeyMainStore)
|
|
||||||
|
|
||||||
// Set a handler Route.
|
|
||||||
baseApp.Router().AddRoute("kvstore", Handler(capKeyMainStore))
|
|
||||||
|
|
||||||
// Load latest version.
|
|
||||||
if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the ABCI server
|
|
||||||
srv, err := server.NewServer("0.0.0.0:26658", "socket", baseApp)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
err = srv.Start()
|
|
||||||
if err != nil {
|
|
||||||
cmn.Exit(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait forever
|
|
||||||
cmn.TrapSignal(func() {
|
|
||||||
// Cleanup
|
|
||||||
err = srv.Stop()
|
|
||||||
if err != nil {
|
|
||||||
cmn.Exit(err.Error())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// KVStore Handler
|
|
||||||
func Handler(storeKey sdk.StoreKey) sdk.Handler {
|
|
||||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
|
||||||
dTx, ok := msg.(kvstoreTx)
|
|
||||||
if !ok {
|
|
||||||
panic("Handler should only receive kvstoreTx")
|
|
||||||
}
|
|
||||||
|
|
||||||
// tx is already unmarshalled
|
|
||||||
key := dTx.key
|
|
||||||
value := dTx.value
|
|
||||||
|
|
||||||
store := ctx.KVStore(storeKey)
|
|
||||||
store.Set(key, value)
|
|
||||||
|
|
||||||
return sdk.Result{
|
|
||||||
Code: 0,
|
|
||||||
Log: fmt.Sprintf("set %s=%s", key, value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
)
|
|
||||||
|
|
||||||
// An sdk.Tx which is its own sdk.Msg.
|
|
||||||
type kvstoreTx struct {
|
|
||||||
key []byte
|
|
||||||
value []byte
|
|
||||||
bytes []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx kvstoreTx) Route() string {
|
|
||||||
return "kvstore"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx kvstoreTx) Type() string {
|
|
||||||
return "kvstore"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx kvstoreTx) GetMsgs() []sdk.Msg {
|
|
||||||
return []sdk.Msg{tx}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx kvstoreTx) GetMemo() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx kvstoreTx) GetSignBytes() []byte {
|
|
||||||
return sdk.MustSortJSON(tx.bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should the app be calling this? Or only handlers?
|
|
||||||
func (tx kvstoreTx) ValidateBasic() sdk.Error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx kvstoreTx) GetSigners() []sdk.AccAddress {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx kvstoreTx) GetSignatures() []auth.StdSignature {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// takes raw transaction bytes and decodes them into an sdk.Tx. An sdk.Tx has
|
|
||||||
// all the signatures and can be used to authenticate.
|
|
||||||
func decodeTx(txBytes []byte) (sdk.Tx, sdk.Error) {
|
|
||||||
var tx sdk.Tx
|
|
||||||
|
|
||||||
split := bytes.Split(txBytes, []byte("="))
|
|
||||||
if len(split) == 1 {
|
|
||||||
k := split[0]
|
|
||||||
tx = kvstoreTx{k, k, txBytes}
|
|
||||||
} else if len(split) == 2 {
|
|
||||||
k, v := split[0], split[1]
|
|
||||||
tx = kvstoreTx{k, v, txBytes}
|
|
||||||
} else {
|
|
||||||
return nil, sdk.ErrTxDecode("too many =")
|
|
||||||
}
|
|
||||||
|
|
||||||
return tx, nil
|
|
||||||
}
|
|
|
@ -77,26 +77,14 @@ var sumValue := externalModule.ComputeSumValue(*account)
|
||||||
```
|
```
|
||||||
|
|
||||||
In the Cosmos SDK, you can see the application of this principle in the
|
In the Cosmos SDK, you can see the application of this principle in the
|
||||||
[basecoin examples folder](../examples/basecoin).
|
[gaia app](../gaia/app/app.go).
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// File: cosmos-sdk/docs/examples/basecoin/app/init_handlers.go
|
// register message routes
|
||||||
package app
|
app.Router().
|
||||||
|
AddRoute(bank.RouterKey, bank.NewHandler(app.bankKeeper)).
|
||||||
import (
|
AddRoute(staking.RouterKey, staking.NewHandler(app.stakingKeeper)).
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
AddRoute(distr.RouterKey, distr.NewHandler(app.distrKeeper)).
|
||||||
"github.com/cosmos/cosmos-sdk/x/sketchy"
|
AddRoute(slashing.RouterKey, slashing.NewHandler(app.slashingKeeper)).
|
||||||
)
|
AddRoute(gov.RouterKey, gov.NewHandler(app.govKeeper))
|
||||||
|
|
||||||
func (app *BasecoinApp) initRouterHandlers() {
|
|
||||||
|
|
||||||
// All handlers must be added here.
|
|
||||||
// The order matters.
|
|
||||||
app.router.AddRoute("bank", bank.NewHandler(app.accountKeeper))
|
|
||||||
app.router.AddRoute("sketchy", sketchy.NewHandler())
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
In the Basecoin example, the sketchy handler isn't provided an account
|
|
||||||
mapper, which does provide the bank handler with the capability (in
|
|
||||||
conjunction with the context of a transaction run).
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ type BaseConfig struct {
|
||||||
// The minimum gas prices a validator is willing to accept for processing a
|
// The minimum gas prices a validator is willing to accept for processing a
|
||||||
// transaction. A transaction's fees must meet the minimum of each denomination
|
// transaction. A transaction's fees must meet the minimum of each denomination
|
||||||
// specified in this config (e.g. 0.01photino,0.0001stake).
|
// specified in this config (e.g. 0.01photino,0.0001stake).
|
||||||
MinGasPrices string `mapstructure:"minimum_gas_prices"`
|
MinGasPrices string `mapstructure:"minimum-gas-prices"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config defines the server's top level configuration
|
// Config defines the server's top level configuration
|
||||||
|
|
|
@ -16,7 +16,7 @@ const defaultConfigTemplate = `# This is a TOML config file.
|
||||||
# The minimum gas prices a validator is willing to accept for processing a
|
# The minimum gas prices a validator is willing to accept for processing a
|
||||||
# transaction. A transaction's fees must meet the minimum of each denomination
|
# transaction. A transaction's fees must meet the minimum of each denomination
|
||||||
# specified in this config (e.g. 0.01photino,0.0001stake).
|
# specified in this config (e.g. 0.01photino,0.0001stake).
|
||||||
minimum_gas_prices = "{{ .BaseConfig.MinGasPrices }}"
|
minimum-gas-prices = "{{ .BaseConfig.MinGasPrices }}"
|
||||||
`
|
`
|
||||||
|
|
||||||
var configTemplate *template.Template
|
var configTemplate *template.Template
|
||||||
|
|
|
@ -21,7 +21,7 @@ const (
|
||||||
flagAddress = "address"
|
flagAddress = "address"
|
||||||
flagTraceStore = "trace-store"
|
flagTraceStore = "trace-store"
|
||||||
flagPruning = "pruning"
|
flagPruning = "pruning"
|
||||||
FlagMinGasPrices = "minimum_gas_prices"
|
FlagMinGasPrices = "minimum-gas-prices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartCmd runs the service passed in, either stand-alone or in-process with
|
// StartCmd runs the service passed in, either stand-alone or in-process with
|
||||||
|
|
|
@ -6,14 +6,13 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
|
|
||||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||||
|
"github.com/tendermint/tendermint/libs/cli"
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
pvm "github.com/tendermint/tendermint/privval"
|
pvm "github.com/tendermint/tendermint/privval"
|
||||||
tversion "github.com/tendermint/tendermint/version"
|
tversion "github.com/tendermint/tendermint/version"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,7 +54,7 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command {
|
||||||
cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
||||||
valPubKey := privValidator.GetPubKey()
|
valPubKey := privValidator.GetPubKey()
|
||||||
|
|
||||||
if viper.GetBool(client.FlagJson) {
|
if viper.GetString(cli.OutputFlag) == "json" {
|
||||||
return printlnJSON(valPubKey)
|
return printlnJSON(valPubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +67,8 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cmd.Flags().Bool(client.FlagJson, false, "get machine parseable output")
|
|
||||||
|
cmd.Flags().StringP(cli.OutputFlag, "o", "text", "Output format (text|json)")
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ func ShowAddressCmd(ctx *Context) *cobra.Command {
|
||||||
cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
||||||
valConsAddr := (sdk.ConsAddress)(privValidator.GetAddress())
|
valConsAddr := (sdk.ConsAddress)(privValidator.GetAddress())
|
||||||
|
|
||||||
if viper.GetBool(client.FlagJson) {
|
if viper.GetString(cli.OutputFlag) == "json" {
|
||||||
return printlnJSON(valConsAddr)
|
return printlnJSON(valConsAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ func ShowAddressCmd(ctx *Context) *cobra.Command {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().Bool(client.FlagJson, false, "get machine parseable output")
|
cmd.Flags().StringP(cli.OutputFlag, "o", "text", "Output format (text|json)")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Note: Bucky, I know you want to kill bash tests.
|
|
||||||
# Please show me how to do an alternative to this.
|
|
||||||
# I would rather get code running before I leave than
|
|
||||||
# fight trying to invent some new test harness that
|
|
||||||
# no one else will understand.
|
|
||||||
#
|
|
||||||
# Thus, I leave this as an exercise to the reader to
|
|
||||||
# port into a non-bash version. And I don't do it proper...
|
|
||||||
# just automate my manual tests
|
|
||||||
|
|
||||||
# WARNING!!!
|
|
||||||
rm -rf ~/.basecoind ~/.basecli
|
|
||||||
cd $GOPATH/src/github.com/cosmos/cosmos-sdk
|
|
||||||
# make get_vendor_deps
|
|
||||||
make build
|
|
||||||
|
|
||||||
# init stuff
|
|
||||||
SEED=`./build/basecoind init | tail -1`
|
|
||||||
PASS='some-silly-123'
|
|
||||||
(echo $PASS; echo $SEED) | ./build/basecli keys add demo --recover
|
|
||||||
ADDR=`./build/basecli keys show demo | cut -f3`
|
|
||||||
echo "Recovered seed for demo:" $ADDR
|
|
||||||
|
|
||||||
# start up server
|
|
||||||
./build/basecoind start > ~/.basecoind/basecoind.log 2>&1 &
|
|
||||||
sleep 5
|
|
||||||
PID_SERVER=$!
|
|
||||||
|
|
||||||
# query original state
|
|
||||||
TO='ABCAFE00DEADBEEF00CAFE00DEADBEEF00CAFE00'
|
|
||||||
echo; echo "My account:" $ADDR
|
|
||||||
./build/basecli account $ADDR
|
|
||||||
echo; echo "Empty account:" $TO
|
|
||||||
./build/basecli account $TO
|
|
||||||
|
|
||||||
# send some money
|
|
||||||
TX=`echo $PASS | ./build/basecli send --to=$TO --amount=1000mycoin --from=demo --seq=0`
|
|
||||||
echo; echo "SendTx"; echo $TX
|
|
||||||
HASH=`echo $TX | cut -d' ' -f6`
|
|
||||||
echo "tx hash:" $HASH
|
|
||||||
|
|
||||||
# let some blocks come up....
|
|
||||||
./build/basecli status | jq .latest_block_height
|
|
||||||
sleep 2
|
|
||||||
./build/basecli status | jq .latest_block_height
|
|
||||||
|
|
||||||
# balances change
|
|
||||||
echo; echo "My account went down"
|
|
||||||
./build/basecli account $ADDR
|
|
||||||
echo; echo "Empty account got some cash"
|
|
||||||
./build/basecli account $TO
|
|
||||||
|
|
||||||
# query original tx
|
|
||||||
echo; echo "View tx"
|
|
||||||
./build/basecli tx $HASH
|
|
||||||
|
|
||||||
# wait a bit then dump out some blockchain state
|
|
||||||
sleep 10
|
|
||||||
./build/basecli status --trace
|
|
||||||
./build/basecli block --trace
|
|
||||||
./build/basecli validatorset --trace
|
|
||||||
|
|
||||||
# shutdown, but add a sleep if you want to manually run some cli scripts
|
|
||||||
# against this server before it goes away
|
|
||||||
# sleep 120
|
|
||||||
kill $PID_SERVER
|
|
||||||
|
|
|
@ -67,7 +67,6 @@ var _ Account = (*BaseAccount)(nil)
|
||||||
|
|
||||||
// BaseAccount - a base account structure.
|
// BaseAccount - a base account structure.
|
||||||
// This can be extended by embedding within in your AppAccount.
|
// This can be extended by embedding within in your AppAccount.
|
||||||
// There are examples of this in: examples/basecoin/types/account.go.
|
|
||||||
// However one doesn't have to use BaseAccount as long as your struct
|
// However one doesn't have to use BaseAccount as long as your struct
|
||||||
// implements Account.
|
// implements Account.
|
||||||
type BaseAccount struct {
|
type BaseAccount struct {
|
||||||
|
|
|
@ -211,7 +211,7 @@ func ProcessPubKey(acc Account, sig StdSignature, simulate bool) (crypto.PubKey,
|
||||||
|
|
||||||
if !bytes.Equal(pubKey.Address(), acc.GetAddress()) {
|
if !bytes.Equal(pubKey.Address(), acc.GetAddress()) {
|
||||||
return nil, sdk.ErrInvalidPubKey(
|
return nil, sdk.ErrInvalidPubKey(
|
||||||
fmt.Sprintf("PubKey does not match Signer address %v", acc.GetAddress())).Result()
|
fmt.Sprintf("PubKey does not match Signer address %s", acc.GetAddress())).Result()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue