Merge branch 'master' into gov_split_vote_weighted_vote
This commit is contained in:
commit
9635a99e2f
@ -15,12 +15,15 @@ coverage:
|
||||
threshold: 1% # allow this much decrease on project
|
||||
app:
|
||||
target: 70%
|
||||
flags: app
|
||||
flags:
|
||||
- app
|
||||
modules:
|
||||
target: 70%
|
||||
flags: modules
|
||||
flags:
|
||||
- modules
|
||||
client:
|
||||
flags: client
|
||||
flags:
|
||||
- client
|
||||
changes: false
|
||||
|
||||
comment:
|
||||
|
||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@ -144,6 +144,7 @@ jobs:
|
||||
run: |
|
||||
excludelist="$(find ./ -type f -name '*.go' | xargs grep -l 'DONTCOVER')"
|
||||
excludelist+=" $(find ./ -type f -name '*.pb.go')"
|
||||
excludelist+=" $(find ./ -type f -name '*.pb.gw.go')"
|
||||
excludelist+=" $(find ./ -type f -path './tests/mocks/*.go')"
|
||||
for filename in ${excludelist}; do
|
||||
filename=$(echo $filename | sed 's/^./github.com\/cosmos\/cosmos-sdk/g')
|
||||
@ -151,7 +152,7 @@ jobs:
|
||||
sed -i.bak "/$(echo $filename | sed 's/\//\\\//g')/d" coverage.txt
|
||||
done
|
||||
if: env.GIT_DIFF
|
||||
- uses: codecov/codecov-action@v1.0.14
|
||||
- uses: codecov/codecov-action@v1.0.15
|
||||
with:
|
||||
file: ./coverage.txt
|
||||
if: env.GIT_DIFF
|
||||
@ -230,6 +231,9 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2.1.3
|
||||
with:
|
||||
go-version: 1.15
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
id: git_diff
|
||||
with:
|
||||
|
||||
@ -37,10 +37,17 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
## [Unreleased]
|
||||
|
||||
### Improvements
|
||||
- (SDK) [\#7925](https://github.com/cosmos/cosmos-sdk/pull/7925) Updated dependencies to use gRPC v1.33.2
|
||||
* (SDK) [\#7925](https://github.com/cosmos/cosmos-sdk/pull/7925) Updated dependencies to use gRPC v1.33.2
|
||||
* Updated gRPC dependency to v1.33.2
|
||||
* Updated iavl dependency to v0.15-rc2
|
||||
* (version) [\#7848](https://github.com/cosmos/cosmos-sdk/pull/7848) [\#7941](https://github.com/cosmos/cosmos-sdk/pull/7941) `version --long` output now shows the list of build dependencies and replaced build dependencies.
|
||||
|
||||
### State Machine Breaking Changes
|
||||
* (x/upgrade) [\#7979](https://github.com/cosmos/cosmos-sdk/pull/7979) keeper pubkey storage serialization migration from bech32 to protobuf.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* (crypto) [\#7966](https://github.com/cosmos/cosmos-sdk/issues/7966) `Bip44Params` `String()` function now correctly returns the absolute HD path by adding the `m/` prefix.
|
||||
|
||||
## [v0.40.0-rc3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0-rc3) - 2020-11-06
|
||||
|
||||
|
||||
48
Makefile
48
Makefile
@ -10,7 +10,8 @@ BUILDDIR ?= $(CURDIR)/build
|
||||
SIMAPP = ./simapp
|
||||
MOCKS_DIR = $(CURDIR)/tests/mocks
|
||||
HTTPS_GIT := https://github.com/cosmos/cosmos-sdk.git
|
||||
DOCKER_BUF := docker run -v $(CURDIR):/workspace --workdir /workspace bufbuild/buf
|
||||
DOCKER := $(shell which docker)
|
||||
DOCKER_BUF := $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace bufbuild/buf
|
||||
|
||||
export GO111MODULE = on
|
||||
|
||||
@ -112,26 +113,26 @@ $(BUILDDIR)/:
|
||||
mkdir -p $(BUILDDIR)/
|
||||
|
||||
build-simd-all: go.sum
|
||||
docker rm latest-build || true
|
||||
docker run --volume=$(CURDIR):/sources:ro \
|
||||
$(DOCKER) rm latest-build || true
|
||||
$(DOCKER) run --volume=$(CURDIR):/sources:ro \
|
||||
--env TARGET_PLATFORMS='linux/amd64 darwin/amd64 linux/arm64 windows/amd64' \
|
||||
--env APP=simd \
|
||||
--env VERSION=$(VERSION) \
|
||||
--env COMMIT=$(COMMIT) \
|
||||
--env LEDGER_ENABLED=$(LEDGER_ENABLED) \
|
||||
--name latest-build cosmossdk/rbuilder:latest
|
||||
docker cp -a latest-build:/home/builder/artifacts/ $(CURDIR)/
|
||||
$(DOCKER) cp -a latest-build:/home/builder/artifacts/ $(CURDIR)/
|
||||
|
||||
build-simd-linux: go.sum $(BUILDDIR)/
|
||||
docker rm latest-build || true
|
||||
docker run --volume=$(CURDIR):/sources:ro \
|
||||
$(DOCKER) rm latest-build || true
|
||||
$(DOCKER) run --volume=$(CURDIR):/sources:ro \
|
||||
--env TARGET_PLATFORMS='linux/amd64' \
|
||||
--env APP=simd \
|
||||
--env VERSION=$(VERSION) \
|
||||
--env COMMIT=$(COMMIT) \
|
||||
--env LEDGER_ENABLED=false \
|
||||
--name latest-build cosmossdk/rbuilder:latest
|
||||
docker cp -a latest-build:/home/builder/artifacts/ $(CURDIR)/
|
||||
$(DOCKER) cp -a latest-build:/home/builder/artifacts/ $(CURDIR)/
|
||||
cp artifacts/simd-*-linux-amd64 $(BUILDDIR)/simd
|
||||
|
||||
cosmovisor:
|
||||
@ -333,12 +334,12 @@ format:
|
||||
DEVDOC_SAVE = docker commit `docker ps -a -n 1 -q` devdoc:local
|
||||
|
||||
devdoc-init:
|
||||
docker run -it -v "$(CURDIR):/go/src/github.com/cosmos/cosmos-sdk" -w "/go/src/github.com/cosmos/cosmos-sdk" tendermint/devdoc echo
|
||||
$(DOCKER) run -it -v "$(CURDIR):/go/src/github.com/cosmos/cosmos-sdk" -w "/go/src/github.com/cosmos/cosmos-sdk" tendermint/devdoc echo
|
||||
# TODO make this safer
|
||||
$(call DEVDOC_SAVE)
|
||||
|
||||
devdoc:
|
||||
docker run -it -v "$(CURDIR):/go/src/github.com/cosmos/cosmos-sdk" -w "/go/src/github.com/cosmos/cosmos-sdk" devdoc:local bash
|
||||
$(DOCKER) run -it -v "$(CURDIR):/go/src/github.com/cosmos/cosmos-sdk" -w "/go/src/github.com/cosmos/cosmos-sdk" devdoc:local bash
|
||||
|
||||
devdoc-save:
|
||||
# TODO make this safer
|
||||
@ -360,17 +361,17 @@ proto-all: proto-format proto-lint proto-check-breaking proto-gen
|
||||
|
||||
proto-gen:
|
||||
@echo "Generating Protobuf files"
|
||||
docker run -v $(CURDIR):/workspace --workdir /workspace tendermintdev/sdk-proto-gen sh ./scripts/protocgen.sh
|
||||
$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace tendermintdev/sdk-proto-gen sh ./scripts/protocgen.sh
|
||||
|
||||
proto-format:
|
||||
@echo "Formatting Protobuf files"
|
||||
docker run -v $(CURDIR):/workspace \
|
||||
$(DOCKER) run --rm -v $(CURDIR):/workspace \
|
||||
--workdir /workspace tendermintdev/docker-build-proto \
|
||||
find ./ -not -path "./third_party/*" -name *.proto -exec clang-format -i {} \;
|
||||
|
||||
# This generates the SDK's custom wrapper for google.protobuf.Any. It should only be run manually when needed
|
||||
proto-gen-any:
|
||||
docker run -v $(CURDIR):/workspace --workdir /workspace tendermintdev/sdk-proto-gen sh ./scripts/protocgen-any.sh
|
||||
$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace tendermintdev/sdk-proto-gen sh ./scripts/protocgen-any.sh
|
||||
|
||||
proto-swagger-gen:
|
||||
@./scripts/protoc-swagger-gen.sh
|
||||
@ -381,16 +382,17 @@ proto-lint:
|
||||
proto-check-breaking:
|
||||
@$(DOCKER_BUF) check breaking --against-input $(HTTPS_GIT)#branch=master
|
||||
|
||||
TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.0-rc6/proto/tendermint
|
||||
GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos
|
||||
COSMOS_PROTO_URL = https://raw.githubusercontent.com/regen-network/cosmos-proto/master
|
||||
CONFIO_URL = https://raw.githubusercontent.com/confio/ics23/v0.6.3
|
||||
TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.0-rc6/proto/tendermint
|
||||
GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos
|
||||
COSMOS_PROTO_URL = https://raw.githubusercontent.com/regen-network/cosmos-proto/master
|
||||
CONFIO_URL = https://raw.githubusercontent.com/confio/ics23/v0.6.3
|
||||
|
||||
TM_CRYPTO_TYPES = third_party/proto/tendermint/crypto
|
||||
TM_ABCI_TYPES = third_party/proto/tendermint/abci
|
||||
TM_TYPES = third_party/proto/tendermint/types
|
||||
TM_VERSION = third_party/proto/tendermint/version
|
||||
TM_LIBS = third_party/proto/tendermint/libs/bits
|
||||
TM_TYPES = third_party/proto/tendermint/types
|
||||
TM_VERSION = third_party/proto/tendermint/version
|
||||
TM_LIBS = third_party/proto/tendermint/libs/bits
|
||||
TM_P2P = third_party/proto/tendermint/p2p
|
||||
|
||||
GOGO_PROTO_TYPES = third_party/proto/gogoproto
|
||||
COSMOS_PROTO_TYPES = third_party/proto/cosmos_proto
|
||||
@ -418,6 +420,7 @@ proto-update-deps:
|
||||
@curl -sSL $(TM_URL)/types/evidence.proto > $(TM_TYPES)/evidence.proto
|
||||
@curl -sSL $(TM_URL)/types/params.proto > $(TM_TYPES)/params.proto
|
||||
@curl -sSL $(TM_URL)/types/validator.proto > $(TM_TYPES)/validator.proto
|
||||
@curl -sSL $(TM_URL)/types/block.proto > $(TM_TYPES)/block.proto
|
||||
|
||||
@mkdir -p $(TM_CRYPTO_TYPES)
|
||||
@curl -sSL $(TM_URL)/crypto/proof.proto > $(TM_CRYPTO_TYPES)/proof.proto
|
||||
@ -426,6 +429,9 @@ proto-update-deps:
|
||||
@mkdir -p $(TM_LIBS)
|
||||
@curl -sSL $(TM_URL)/libs/bits/types.proto > $(TM_LIBS)/types.proto
|
||||
|
||||
@mkdir -p $(TM_P2P)
|
||||
@curl -sSL $(TM_URL)/p2p/types.proto > $(TM_P2P)/types.proto
|
||||
|
||||
@mkdir -p $(CONFIO_TYPES)
|
||||
@curl -sSL $(CONFIO_URL)/proofs.proto > $(CONFIO_TYPES)/proofs.proto
|
||||
## insert go package option into proofs.proto file
|
||||
@ -440,8 +446,8 @@ proto-update-deps:
|
||||
|
||||
# Run a 4-node testnet locally
|
||||
localnet-start: build-linux localnet-stop
|
||||
$(if $(shell docker inspect -f '{{ .Id }}' cosmossdk/simd-env 2>/dev/null),$(info found image cosmossdk/simd-env),$(MAKE) -C contrib/images simd-env)
|
||||
if ! [ -f build/node0/simd/config/genesis.json ]; then docker run --rm \
|
||||
$(if $(shell $(DOCKER) inspect -f '{{ .Id }}' cosmossdk/simd-env 2>/dev/null),$(info found image cosmossdk/simd-env),$(MAKE) -C contrib/images simd-env)
|
||||
if ! [ -f build/node0/simd/config/genesis.json ]; then $(DOCKER) run --rm \
|
||||
--user $(shell id -u):$(shell id -g) \
|
||||
-v $(BUILDDIR):/simd:Z \
|
||||
-v /etc/group:/etc/group:ro \
|
||||
|
||||
@ -382,6 +382,11 @@ func (app *BaseApp) snapshot(height int64) {
|
||||
func (app *BaseApp) Query(req abci.RequestQuery) abci.ResponseQuery {
|
||||
defer telemetry.MeasureSince(time.Now(), "abci", "query")
|
||||
|
||||
// when a client did not provide a query height, manually inject the latest
|
||||
if req.Height == 0 {
|
||||
req.Height = app.LastBlockHeight()
|
||||
}
|
||||
|
||||
// handle gRPC routes first rather than calling splitPath because '/' characters
|
||||
// are used as part of gRPC paths
|
||||
if grpcHandler := app.grpcQueryRouter.Route(req.Path); grpcHandler != nil {
|
||||
@ -742,11 +747,6 @@ func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.R
|
||||
|
||||
req.Path = "/" + strings.Join(path[1:], "/")
|
||||
|
||||
// when a client did not provide a query height, manually inject the latest
|
||||
if req.Height == 0 {
|
||||
req.Height = app.LastBlockHeight()
|
||||
}
|
||||
|
||||
if req.Height <= 1 && req.Prove {
|
||||
return sdkerrors.QueryResult(
|
||||
sdkerrors.Wrap(
|
||||
|
||||
1441
client/docs/swagger-ui/swagger.yaml
vendored
1441
client/docs/swagger-ui/swagger.yaml
vendored
File diff suppressed because it is too large
Load Diff
@ -1,22 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// ErrInvalidAccount returns a standardized error reflecting that a given
|
||||
// account address does not exist.
|
||||
func ErrInvalidAccount(addr sdk.AccAddress) error {
|
||||
return fmt.Errorf(`no account with address %s was found in the state.
|
||||
Are you sure there has been a transaction involving it?`, addr)
|
||||
}
|
||||
|
||||
// ErrVerifyCommit returns a common error reflecting that the blockchain commit at a given
|
||||
// height can't be verified. The reason is that the base checkpoint of the certifier is
|
||||
// newer than the given height
|
||||
func ErrVerifyCommit(height int64) error {
|
||||
return fmt.Errorf(`the height of base truststore in the light client is higher than height %d.
|
||||
Can't verify blockchain proof at this height. Please set --trust-node to true and try again`, height)
|
||||
}
|
||||
19
client/grpc/tmservice/block.go
Normal file
19
client/grpc/tmservice/block.go
Normal file
@ -0,0 +1,19 @@
|
||||
package tmservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
)
|
||||
|
||||
func getBlock(clientCtx client.Context, height *int64) (*ctypes.ResultBlock, error) {
|
||||
// get the node
|
||||
node, err := clientCtx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return node.Block(context.Background(), height)
|
||||
}
|
||||
206
client/grpc/tmservice/service.go
Normal file
206
client/grpc/tmservice/service.go
Normal file
@ -0,0 +1,206 @@
|
||||
package tmservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
gogogrpc "github.com/gogo/protobuf/grpc"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
qtypes "github.com/cosmos/cosmos-sdk/types/query"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
)
|
||||
|
||||
// This is the struct that we will implement all the handlers on.
|
||||
type queryServer struct {
|
||||
clientCtx client.Context
|
||||
interfaceRegistry codectypes.InterfaceRegistry
|
||||
}
|
||||
|
||||
var _ qtypes.ServiceServer = queryServer{}
|
||||
|
||||
// NewQueryServer creates a new tendermint query server.
|
||||
func NewQueryServer(clientCtx client.Context, interfaceRegistry codectypes.InterfaceRegistry) qtypes.ServiceServer {
|
||||
return queryServer{
|
||||
clientCtx: clientCtx,
|
||||
interfaceRegistry: interfaceRegistry,
|
||||
}
|
||||
}
|
||||
|
||||
// GetSyncing implements ServiceServer.GetSyncing
|
||||
func (s queryServer) GetSyncing(_ context.Context, _ *qtypes.GetSyncingRequest) (*qtypes.GetSyncingResponse, error) {
|
||||
status, err := getNodeStatus(s.clientCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &qtypes.GetSyncingResponse{
|
||||
Syncing: status.SyncInfo.CatchingUp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetLatestBlock implements ServiceServer.GetLatestBlock
|
||||
func (s queryServer) GetLatestBlock(context.Context, *qtypes.GetLatestBlockRequest) (*qtypes.GetLatestBlockResponse, error) {
|
||||
status, err := getBlock(s.clientCtx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
protoBlockID := status.BlockID.ToProto()
|
||||
protoBlock, err := status.Block.ToProto()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &qtypes.GetLatestBlockResponse{
|
||||
BlockId: &protoBlockID,
|
||||
Block: protoBlock,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetBlockByHeight implements ServiceServer.GetBlockByHeight
|
||||
func (s queryServer) GetBlockByHeight(_ context.Context, req *qtypes.GetBlockByHeightRequest) (*qtypes.GetBlockByHeightResponse, error) {
|
||||
chainHeight, err := rpc.GetChainHeight(s.clientCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.Height > chainHeight {
|
||||
return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length")
|
||||
}
|
||||
|
||||
res, err := getBlock(s.clientCtx, &req.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
protoBlockID := res.BlockID.ToProto()
|
||||
protoBlock, err := res.Block.ToProto()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &qtypes.GetBlockByHeightResponse{
|
||||
BlockId: &protoBlockID,
|
||||
Block: protoBlock,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetLatestValidatorSet implements ServiceServer.GetLatestValidatorSet
|
||||
func (s queryServer) GetLatestValidatorSet(ctx context.Context, req *qtypes.GetLatestValidatorSetRequest) (*qtypes.GetLatestValidatorSetResponse, error) {
|
||||
page, limit, err := qtypes.ParsePagination(req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
validatorsRes, err := rpc.GetValidators(s.clientCtx, nil, &page, &limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputValidatorsRes := &qtypes.GetLatestValidatorSetResponse{
|
||||
BlockHeight: validatorsRes.BlockHeight,
|
||||
Validators: make([]*qtypes.Validator, len(validatorsRes.Validators)),
|
||||
}
|
||||
|
||||
for i, validator := range validatorsRes.Validators {
|
||||
outputValidatorsRes.Validators[i] = &qtypes.Validator{
|
||||
Address: validator.Address,
|
||||
ProposerPriority: validator.ProposerPriority,
|
||||
PubKey: validator.PubKey,
|
||||
VotingPower: validator.VotingPower,
|
||||
}
|
||||
}
|
||||
return outputValidatorsRes, nil
|
||||
}
|
||||
|
||||
// GetValidatorSetByHeight implements ServiceServer.GetValidatorSetByHeight
|
||||
func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *qtypes.GetValidatorSetByHeightRequest) (*qtypes.GetValidatorSetByHeightResponse, error) {
|
||||
page, limit, err := qtypes.ParsePagination(req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chainHeight, err := rpc.GetChainHeight(s.clientCtx)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "failed to parse chain height")
|
||||
}
|
||||
if req.Height > chainHeight {
|
||||
return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length")
|
||||
}
|
||||
|
||||
validatorsRes, err := rpc.GetValidators(s.clientCtx, &req.Height, &page, &limit)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputValidatorsRes := &qtypes.GetValidatorSetByHeightResponse{
|
||||
BlockHeight: validatorsRes.BlockHeight,
|
||||
Validators: make([]*qtypes.Validator, len(validatorsRes.Validators)),
|
||||
}
|
||||
|
||||
for i, validator := range validatorsRes.Validators {
|
||||
outputValidatorsRes.Validators[i] = &qtypes.Validator{
|
||||
Address: validator.Address,
|
||||
ProposerPriority: validator.ProposerPriority,
|
||||
PubKey: validator.PubKey,
|
||||
VotingPower: validator.VotingPower,
|
||||
}
|
||||
}
|
||||
return outputValidatorsRes, nil
|
||||
}
|
||||
|
||||
// GetNodeInfo implements ServiceServer.GetNodeInfo
|
||||
func (s queryServer) GetNodeInfo(ctx context.Context, req *qtypes.GetNodeInfoRequest) (*qtypes.GetNodeInfoResponse, error) {
|
||||
status, err := getNodeStatus(s.clientCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
protoNodeInfo := status.NodeInfo.ToProto()
|
||||
nodeInfo := version.NewInfo()
|
||||
|
||||
deps := make([]*qtypes.Module, len(nodeInfo.BuildDeps))
|
||||
|
||||
for i, dep := range nodeInfo.BuildDeps {
|
||||
deps[i] = &qtypes.Module{
|
||||
Path: dep.Path,
|
||||
Sum: dep.Sum,
|
||||
Version: dep.Version,
|
||||
}
|
||||
}
|
||||
|
||||
resp := qtypes.GetNodeInfoResponse{
|
||||
DefaultNodeInfo: protoNodeInfo,
|
||||
ApplicationVersion: &qtypes.VersionInfo{
|
||||
AppName: nodeInfo.AppName,
|
||||
Name: nodeInfo.Name,
|
||||
GitCommit: nodeInfo.GitCommit,
|
||||
GoVersion: nodeInfo.GoVersion,
|
||||
Version: nodeInfo.Version,
|
||||
BuildTags: nodeInfo.BuildTags,
|
||||
BuildDeps: deps,
|
||||
},
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
// RegisterTendermintService registers the tendermint queries on the gRPC router.
|
||||
func RegisterTendermintService(
|
||||
qrt gogogrpc.Server,
|
||||
clientCtx client.Context,
|
||||
interfaceRegistry codectypes.InterfaceRegistry,
|
||||
) {
|
||||
qtypes.RegisterServiceServer(
|
||||
qrt,
|
||||
NewQueryServer(clientCtx, interfaceRegistry),
|
||||
)
|
||||
}
|
||||
|
||||
// RegisterGRPCGatewayRoutes mounts the tendermint service's GRPC-gateway routes on the
|
||||
// given Mux.
|
||||
func RegisterGRPCGatewayRoutes(clientConn gogogrpc.ClientConn, mux *runtime.ServeMux) {
|
||||
qtypes.RegisterServiceHandlerClient(context.Background(), mux, qtypes.NewServiceClient(clientConn))
|
||||
}
|
||||
153
client/grpc/tmservice/service_test.go
Normal file
153
client/grpc/tmservice/service_test.go
Normal file
@ -0,0 +1,153 @@
|
||||
package tmservice_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||
qtypes "github.com/cosmos/cosmos-sdk/types/query"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
)
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
cfg network.Config
|
||||
network *network.Network
|
||||
|
||||
queryClient qtypes.ServiceClient
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) SetupSuite() {
|
||||
s.T().Log("setting up integration test suite")
|
||||
|
||||
cfg := network.DefaultConfig()
|
||||
cfg.NumValidators = 1
|
||||
|
||||
s.cfg = cfg
|
||||
s.network = network.New(s.T(), cfg)
|
||||
|
||||
s.Require().NotNil(s.network)
|
||||
|
||||
_, err := s.network.WaitForHeight(1)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.queryClient = qtypes.NewServiceClient(s.network.Validators[0].ClientCtx)
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TearDownSuite() {
|
||||
s.T().Log("tearing down integration test suite")
|
||||
s.network.Cleanup()
|
||||
}
|
||||
|
||||
func (s IntegrationTestSuite) TestQueryNodeInfo() {
|
||||
val := s.network.Validators[0]
|
||||
|
||||
res, err := s.queryClient.GetNodeInfo(context.Background(), &qtypes.GetNodeInfoRequest{})
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res.ApplicationVersion.AppName, version.NewInfo().AppName)
|
||||
|
||||
restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/node_info", val.APIAddress))
|
||||
s.Require().NoError(err)
|
||||
var getInfoRes qtypes.GetNodeInfoResponse
|
||||
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &getInfoRes))
|
||||
s.Require().Equal(getInfoRes.ApplicationVersion.AppName, version.NewInfo().AppName)
|
||||
}
|
||||
|
||||
func (s IntegrationTestSuite) TestQuerySyncing() {
|
||||
val := s.network.Validators[0]
|
||||
|
||||
_, err := s.queryClient.GetSyncing(context.Background(), &qtypes.GetSyncingRequest{})
|
||||
s.Require().NoError(err)
|
||||
|
||||
restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/syncing", val.APIAddress))
|
||||
s.Require().NoError(err)
|
||||
var syncingRes qtypes.GetSyncingResponse
|
||||
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &syncingRes))
|
||||
}
|
||||
|
||||
func (s IntegrationTestSuite) TestQueryLatestBlock() {
|
||||
val := s.network.Validators[0]
|
||||
|
||||
_, err := s.queryClient.GetLatestBlock(context.Background(), &qtypes.GetLatestBlockRequest{})
|
||||
s.Require().NoError(err)
|
||||
|
||||
restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/blocks/latest", val.APIAddress))
|
||||
s.Require().NoError(err)
|
||||
var blockInfoRes qtypes.GetLatestBlockResponse
|
||||
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &blockInfoRes))
|
||||
}
|
||||
|
||||
func (s IntegrationTestSuite) TestQueryBlockByHeight() {
|
||||
val := s.network.Validators[0]
|
||||
_, err := s.queryClient.GetBlockByHeight(context.Background(), &qtypes.GetBlockByHeightRequest{Height: 1})
|
||||
s.Require().NoError(err)
|
||||
|
||||
restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/blocks/%d", val.APIAddress, 1))
|
||||
s.Require().NoError(err)
|
||||
var blockInfoRes qtypes.GetBlockByHeightResponse
|
||||
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &blockInfoRes))
|
||||
}
|
||||
|
||||
func (s IntegrationTestSuite) TestQueryLatestValidatorSet() {
|
||||
val := s.network.Validators[0]
|
||||
|
||||
// nil pagination
|
||||
_, err := s.queryClient.GetLatestValidatorSet(context.Background(), &qtypes.GetLatestValidatorSetRequest{
|
||||
Pagination: nil,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
//with pagination
|
||||
_, err = s.queryClient.GetLatestValidatorSet(context.Background(), &qtypes.GetLatestValidatorSetRequest{Pagination: &qtypes.PageRequest{
|
||||
Offset: 0,
|
||||
Limit: 10,
|
||||
}})
|
||||
s.Require().NoError(err)
|
||||
|
||||
// rest request without pagination
|
||||
_, err = rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validators/latest", val.APIAddress))
|
||||
s.Require().NoError(err)
|
||||
|
||||
// rest request with pagination
|
||||
restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validators/latest?pagination.offset=%d&pagination.limit=%d", val.APIAddress, 0, 1))
|
||||
s.Require().NoError(err)
|
||||
var validatorSetRes qtypes.GetLatestValidatorSetResponse
|
||||
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &validatorSetRes))
|
||||
}
|
||||
|
||||
func (s IntegrationTestSuite) TestQueryValidatorSetByHeight() {
|
||||
val := s.network.Validators[0]
|
||||
|
||||
// nil pagination
|
||||
_, err := s.queryClient.GetValidatorSetByHeight(context.Background(), &qtypes.GetValidatorSetByHeightRequest{
|
||||
Height: 1,
|
||||
Pagination: nil,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
_, err = s.queryClient.GetValidatorSetByHeight(context.Background(), &qtypes.GetValidatorSetByHeightRequest{
|
||||
Height: 1,
|
||||
Pagination: &qtypes.PageRequest{
|
||||
Offset: 0,
|
||||
Limit: 10,
|
||||
}})
|
||||
s.Require().NoError(err)
|
||||
|
||||
// no pagination rest
|
||||
_, err = rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validators/%d", val.APIAddress, 1))
|
||||
s.Require().NoError(err)
|
||||
|
||||
// rest query with pagination
|
||||
restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/base/tendermint/v1beta1/validators/%d?pagination.offset=%d&pagination.limit=%d", val.APIAddress, 1, 0, 1))
|
||||
var validatorSetRes qtypes.GetValidatorSetByHeightResponse
|
||||
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &validatorSetRes))
|
||||
}
|
||||
|
||||
func TestIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
}
|
||||
17
client/grpc/tmservice/status.go
Normal file
17
client/grpc/tmservice/status.go
Normal file
@ -0,0 +1,17 @@
|
||||
package tmservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
)
|
||||
|
||||
func getNodeStatus(clientCtx client.Context) (*ctypes.ResultStatus, error) {
|
||||
node, err := clientCtx.GetNode()
|
||||
if err != nil {
|
||||
return &ctypes.ResultStatus{}, err
|
||||
}
|
||||
return node.Status(context.Background())
|
||||
}
|
||||
@ -6,12 +6,17 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// DeprecationURL is the URL for migrating deprecated REST endpoints to newer ones.
|
||||
// TODO Switch to `/` (not `/master`) once v0.40 docs are deployed.
|
||||
// https://github.com/cosmos/cosmos-sdk/issues/8019
|
||||
const DeprecationURL = "https://docs.cosmos.network/master/migrations/rest.html"
|
||||
|
||||
// addHTTPDeprecationHeaders is a mux middleware function for adding HTTP
|
||||
// Deprecation headers to a http handler
|
||||
func addHTTPDeprecationHeaders(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Deprecation", "true")
|
||||
w.Header().Set("Link", "<https://docs.cosmos.network/v0.40/interfaces/rest.html>; rel=\"deprecation\"")
|
||||
w.Header().Set("Link", "<"+DeprecationURL+">; rel=\"deprecation\"")
|
||||
w.Header().Set("Warning", "199 - \"this endpoint is deprecated and may not work as before, see deprecation link for more info\"")
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
|
||||
@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/codec/legacy"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
)
|
||||
|
||||
@ -68,7 +67,7 @@ func getBlock(clientCtx client.Context, height *int64) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return legacy.Cdc.MarshalJSON(res)
|
||||
return clientCtx.LegacyAmino.MarshalJSON(res)
|
||||
}
|
||||
|
||||
// get the current blockchain height
|
||||
|
||||
47
client/rpc/rpc_test.go
Normal file
47
client/rpc/rpc_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package rpc_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||
)
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
network *network.Network
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) SetupSuite() {
|
||||
s.T().Log("setting up integration test suite")
|
||||
|
||||
s.network = network.New(s.T(), network.DefaultConfig())
|
||||
s.Require().NotNil(s.network)
|
||||
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TearDownSuite() {
|
||||
s.T().Log("tearing down integration test suite")
|
||||
s.network.Cleanup()
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestStatusCommand() {
|
||||
val0 := s.network.Validators[0]
|
||||
cmd := rpc.StatusCommand()
|
||||
|
||||
out, err := clitestutil.ExecTestCLICmd(val0.ClientCtx, cmd, []string{})
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Make sure the output has the validator moniker.
|
||||
s.Require().Contains(out.String(), fmt.Sprintf("\"moniker\":\"%s\"", val0.Moniker))
|
||||
}
|
||||
|
||||
func TestIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
}
|
||||
@ -2,22 +2,38 @@ package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/codec/legacy"
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
)
|
||||
|
||||
// ValidatorInfo is info about the node's validator, same as Tendermint,
|
||||
// except that we use our own PubKey.
|
||||
type validatorInfo struct {
|
||||
Address bytes.HexBytes
|
||||
PubKey cryptotypes.PubKey
|
||||
VotingPower int64
|
||||
}
|
||||
|
||||
// ResultStatus is node's info, same as Tendermint, except that we use our own
|
||||
// PubKey.
|
||||
type resultStatus struct {
|
||||
NodeInfo p2p.DefaultNodeInfo
|
||||
SyncInfo ctypes.SyncInfo
|
||||
ValidatorInfo validatorInfo
|
||||
}
|
||||
|
||||
// StatusCommand returns the command to return the status of the network.
|
||||
func StatusCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
@ -31,12 +47,27 @@ func StatusCommand() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
output, err := legacy.Cdc.MarshalJSON(status)
|
||||
// `status` has TM pubkeys, we need to convert them to our pubkeys.
|
||||
pk, err := cryptocodec.FromTmPubKeyInterface(status.ValidatorInfo.PubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statusWithPk := resultStatus{
|
||||
NodeInfo: status.NodeInfo,
|
||||
SyncInfo: status.SyncInfo,
|
||||
ValidatorInfo: validatorInfo{
|
||||
Address: status.ValidatorInfo.Address,
|
||||
PubKey: pk,
|
||||
VotingPower: status.ValidatorInfo.VotingPower,
|
||||
},
|
||||
}
|
||||
|
||||
output, err := clientCtx.LegacyAmino.MarshalJSON(statusWithPk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(output))
|
||||
cmd.Println(string(output))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ func (f Factory) WithGas(gas uint64) Factory {
|
||||
|
||||
// WithFees returns a copy of the Factory with an updated fee.
|
||||
func (f Factory) WithFees(fees string) Factory {
|
||||
parsedFees, err := sdk.ParseCoins(fees)
|
||||
parsedFees, err := sdk.ParseCoinsNormalized(fees)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -61,6 +61,7 @@ func CopyTx(tx signing.Tx, builder client.TxBuilder, ignoreSignatureError bool)
|
||||
builder.SetMemo(tx.GetMemo())
|
||||
builder.SetFeeAmount(tx.GetFee())
|
||||
builder.SetGasLimit(tx.GetGas())
|
||||
builder.SetTimeoutHeight(tx.GetTimeoutHeight())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -21,8 +21,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
memo = "waboom"
|
||||
gas = uint64(10000)
|
||||
memo = "waboom"
|
||||
gas = uint64(10000)
|
||||
timeoutHeight = 5
|
||||
)
|
||||
|
||||
var (
|
||||
@ -47,6 +48,7 @@ func buildTestTx(t *testing.T, builder client.TxBuilder) {
|
||||
require.NoError(t, err)
|
||||
err = builder.SetSignatures(sig)
|
||||
require.NoError(t, err)
|
||||
builder.SetTimeoutHeight(timeoutHeight)
|
||||
}
|
||||
|
||||
type TestSuite struct {
|
||||
@ -105,6 +107,7 @@ func (s *TestSuite) TestConvertTxToStdTx() {
|
||||
s.Require().Equal(gas, stdTx.Fee.Gas)
|
||||
s.Require().Equal(fee, stdTx.Fee.Amount)
|
||||
s.Require().Equal(msg, stdTx.Msgs[0])
|
||||
s.Require().Equal(timeoutHeight, stdTx.TimeoutHeight)
|
||||
s.Require().Equal(sig.PubKey, stdTx.Signatures[0].PubKey)
|
||||
s.Require().Equal(sig.Data.(*signing2.SingleSignatureData).Signature, stdTx.Signatures[0].Signature)
|
||||
|
||||
@ -123,6 +126,7 @@ func (s *TestSuite) TestConvertTxToStdTx() {
|
||||
s.Require().Equal(gas, stdTx.Fee.Gas)
|
||||
s.Require().Equal(fee, stdTx.Fee.Amount)
|
||||
s.Require().Equal(msg, stdTx.Msgs[0])
|
||||
s.Require().Equal(timeoutHeight, stdTx.TimeoutHeight)
|
||||
s.Require().Empty(stdTx.Signatures)
|
||||
|
||||
// std tx
|
||||
|
||||
@ -27,7 +27,7 @@ type addrData struct {
|
||||
}
|
||||
|
||||
func TestFullFundraiserPath(t *testing.T) {
|
||||
require.Equal(t, "44'/118'/0'/0/0", hd.NewFundraiserParams(0, 118, 0).String())
|
||||
require.Equal(t, "m/44'/118'/0'/0/0", hd.NewFundraiserParams(0, 118, 0).String())
|
||||
}
|
||||
|
||||
func initFundraiserTestVectors(t *testing.T) []addrData {
|
||||
@ -63,8 +63,9 @@ func TestFundraiserCompatibility(t *testing.T) {
|
||||
t.Logf("ROUND: %d MNEMONIC: %s", i, d.Mnemonic)
|
||||
|
||||
master, ch := hd.ComputeMastersFromSeed(seed)
|
||||
priv, err := hd.DerivePrivateKeyForPath(master, ch, "44'/118'/0'/0/0")
|
||||
priv, err := hd.DerivePrivateKeyForPath(master, ch, "m/44'/118'/0'/0/0")
|
||||
require.NoError(t, err)
|
||||
|
||||
privKey := &secp256k1.PrivKey{Key: priv}
|
||||
pub := privKey.PubKey()
|
||||
|
||||
|
||||
@ -36,52 +36,66 @@ func NewParams(purpose, coinType, account uint32, change bool, addressIdx uint32
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the BIP44 path and unmarshal into the struct.
|
||||
// NewParamsFromPath parses the BIP44 path and unmarshals it into a Bip44Params. It supports both
|
||||
// absolute and relative paths.
|
||||
func NewParamsFromPath(path string) (*BIP44Params, error) {
|
||||
spl := strings.Split(path, "/")
|
||||
|
||||
// Handle absolute or relative paths
|
||||
switch {
|
||||
case spl[0] == path:
|
||||
return nil, fmt.Errorf("path %s doesn't contain '/' separators", path)
|
||||
|
||||
case strings.TrimSpace(spl[0]) == "":
|
||||
return nil, fmt.Errorf("ambiguous path %s: use 'm/' prefix for absolute paths, or no leading '/' for relative ones", path)
|
||||
|
||||
case strings.TrimSpace(spl[0]) == "m":
|
||||
spl = spl[1:]
|
||||
}
|
||||
|
||||
if len(spl) != 5 {
|
||||
return nil, fmt.Errorf("path length is wrong. Expected 5, got %d", len(spl))
|
||||
return nil, fmt.Errorf("invalid path length %s", path)
|
||||
}
|
||||
|
||||
// Check items can be parsed
|
||||
purpose, err := hardenedInt(spl[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("invalid HD path purpose %s: %w", spl[0], err)
|
||||
}
|
||||
|
||||
coinType, err := hardenedInt(spl[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("invalid HD path coin type %s: %w", spl[1], err)
|
||||
}
|
||||
|
||||
account, err := hardenedInt(spl[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("invalid HD path account %s: %w", spl[2], err)
|
||||
}
|
||||
|
||||
change, err := hardenedInt(spl[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("invalid HD path change %s: %w", spl[3], err)
|
||||
}
|
||||
|
||||
addressIdx, err := hardenedInt(spl[4])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("invalid HD path address index %s: %w", spl[4], err)
|
||||
}
|
||||
|
||||
// Confirm valid values
|
||||
if spl[0] != "44'" {
|
||||
return nil, fmt.Errorf("first field in path must be 44', got %v", spl[0])
|
||||
return nil, fmt.Errorf("first field in path must be 44', got %s", spl[0])
|
||||
}
|
||||
|
||||
if !isHardened(spl[1]) || !isHardened(spl[2]) {
|
||||
return nil,
|
||||
fmt.Errorf("second and third field in path must be hardened (ie. contain the suffix ', got %v and %v", spl[1], spl[2])
|
||||
fmt.Errorf("second and third field in path must be hardened (ie. contain the suffix ', got %s and %s", spl[1], spl[2])
|
||||
}
|
||||
|
||||
if isHardened(spl[3]) || isHardened(spl[4]) {
|
||||
return nil,
|
||||
fmt.Errorf("fourth and fifth field in path must not be hardened (ie. not contain the suffix ', got %v and %v", spl[3], spl[4])
|
||||
fmt.Errorf("fourth and fifth field in path must not be hardened (ie. not contain the suffix ', got %s and %s", spl[3], spl[4])
|
||||
}
|
||||
|
||||
if !(change == 0 || change == 1) {
|
||||
@ -135,6 +149,8 @@ func (p BIP44Params) DerivationPath() []uint32 {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the full absolute HD path of the BIP44 (https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) params:
|
||||
// m / purpose' / coin_type' / account' / change / address_index
|
||||
func (p BIP44Params) String() string {
|
||||
var changeStr string
|
||||
if p.Change {
|
||||
@ -142,8 +158,7 @@ func (p BIP44Params) String() string {
|
||||
} else {
|
||||
changeStr = "0"
|
||||
}
|
||||
// m / Purpose' / coin_type' / Account' / Change / address_index
|
||||
return fmt.Sprintf("%d'/%d'/%d'/%s/%d",
|
||||
return fmt.Sprintf("m/%d'/%d'/%d'/%s/%d",
|
||||
p.Purpose,
|
||||
p.CoinType,
|
||||
p.Account,
|
||||
@ -165,6 +180,13 @@ func DerivePrivateKeyForPath(privKeyBytes, chainCode [32]byte, path string) ([]b
|
||||
data := privKeyBytes
|
||||
parts := strings.Split(path, "/")
|
||||
|
||||
switch {
|
||||
case parts[0] == path:
|
||||
return nil, fmt.Errorf("path '%s' doesn't contain '/' separators", path)
|
||||
case strings.TrimSpace(parts[0]) == "m":
|
||||
parts = parts[1:]
|
||||
}
|
||||
|
||||
for _, part := range parts {
|
||||
// do we have an apostrophe?
|
||||
harden := part[len(part)-1:] == "'"
|
||||
@ -178,7 +200,7 @@ func DerivePrivateKeyForPath(privKeyBytes, chainCode [32]byte, path string) ([]b
|
||||
// index values are in the range [0, 1<<31-1] aka [0, max(int32)]
|
||||
idx, err := strconv.ParseUint(part, 10, 31)
|
||||
if err != nil {
|
||||
return []byte{}, fmt.Errorf("invalid BIP 32 path: %s", err)
|
||||
return []byte{}, fmt.Errorf("invalid BIP 32 path %s: %w", path, err)
|
||||
}
|
||||
|
||||
data, chainCode = derivePrivateKey(data, chainCode, uint32(idx), harden)
|
||||
@ -188,7 +210,7 @@ func DerivePrivateKeyForPath(privKeyBytes, chainCode [32]byte, path string) ([]b
|
||||
n := copy(derivedKey, data[:])
|
||||
|
||||
if n != 32 || len(data) != 32 {
|
||||
return []byte{}, fmt.Errorf("expected a (secp256k1) key of length 32, got length: %v", len(data))
|
||||
return []byte{}, fmt.Errorf("expected a key of length 32, got length: %d", len(data))
|
||||
}
|
||||
|
||||
return derivedKey, nil
|
||||
|
||||
@ -9,7 +9,6 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
bip39 "github.com/cosmos/go-bip39"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -20,26 +19,15 @@ func mnemonicToSeed(mnemonic string) []byte {
|
||||
return bip39.NewSeed(mnemonic, defaultBIP39Passphrase)
|
||||
}
|
||||
|
||||
// nolint:govet
|
||||
func ExampleStringifyPathParams() {
|
||||
path := hd.NewParams(44, 0, 0, false, 0)
|
||||
fmt.Println(path.String())
|
||||
path = hd.NewParams(44, 33, 7, true, 9)
|
||||
fmt.Println(path.String())
|
||||
// Output:
|
||||
// 44'/0'/0'/0/0
|
||||
// 44'/33'/7'/1/9
|
||||
}
|
||||
|
||||
func TestStringifyFundraiserPathParams(t *testing.T) {
|
||||
path := hd.NewFundraiserParams(4, types.CoinType, 22)
|
||||
require.Equal(t, "44'/118'/4'/0/22", path.String())
|
||||
require.Equal(t, "m/44'/118'/4'/0/22", path.String())
|
||||
|
||||
path = hd.NewFundraiserParams(4, types.CoinType, 57)
|
||||
require.Equal(t, "44'/118'/4'/0/57", path.String())
|
||||
require.Equal(t, "m/44'/118'/4'/0/57", path.String())
|
||||
|
||||
path = hd.NewFundraiserParams(4, 12345, 57)
|
||||
require.Equal(t, "44'/12345'/4'/0/57", path.String())
|
||||
require.Equal(t, "m/44'/12345'/4'/0/57", path.String())
|
||||
}
|
||||
|
||||
func TestPathToArray(t *testing.T) {
|
||||
@ -55,56 +43,160 @@ func TestParamsFromPath(t *testing.T) {
|
||||
params *hd.BIP44Params
|
||||
path string
|
||||
}{
|
||||
{&hd.BIP44Params{44, 0, 0, false, 0}, "44'/0'/0'/0/0"},
|
||||
{&hd.BIP44Params{44, 1, 0, false, 0}, "44'/1'/0'/0/0"},
|
||||
{&hd.BIP44Params{44, 0, 1, false, 0}, "44'/0'/1'/0/0"},
|
||||
{&hd.BIP44Params{44, 0, 0, true, 0}, "44'/0'/0'/1/0"},
|
||||
{&hd.BIP44Params{44, 0, 0, false, 1}, "44'/0'/0'/0/1"},
|
||||
{&hd.BIP44Params{44, 1, 1, true, 1}, "44'/1'/1'/1/1"},
|
||||
{&hd.BIP44Params{44, 118, 52, true, 41}, "44'/118'/52'/1/41"},
|
||||
{&hd.BIP44Params{44, 0, 0, false, 0}, "m/44'/0'/0'/0/0"},
|
||||
{&hd.BIP44Params{44, 1, 0, false, 0}, "m/44'/1'/0'/0/0"},
|
||||
{&hd.BIP44Params{44, 0, 1, false, 0}, "m/44'/0'/1'/0/0"},
|
||||
{&hd.BIP44Params{44, 0, 0, true, 0}, "m/44'/0'/0'/1/0"},
|
||||
{&hd.BIP44Params{44, 0, 0, false, 1}, "m/44'/0'/0'/0/1"},
|
||||
{&hd.BIP44Params{44, 1, 1, true, 1}, "m/44'/1'/1'/1/1"},
|
||||
{&hd.BIP44Params{44, 118, 52, true, 41}, "m/44'/118'/52'/1/41"},
|
||||
}
|
||||
|
||||
for i, c := range goodCases {
|
||||
params, err := hd.NewParamsFromPath(c.path)
|
||||
errStr := fmt.Sprintf("%d %v", i, c)
|
||||
assert.NoError(t, err, errStr)
|
||||
assert.EqualValues(t, c.params, params, errStr)
|
||||
assert.Equal(t, c.path, c.params.String())
|
||||
require.NoError(t, err, errStr)
|
||||
require.EqualValues(t, c.params, params, errStr)
|
||||
require.Equal(t, c.path, c.params.String())
|
||||
}
|
||||
|
||||
badCases := []struct {
|
||||
path string
|
||||
}{
|
||||
{"43'/0'/0'/0/0"}, // doesnt start with 44
|
||||
{"44'/1'/0'/0/0/5"}, // too many fields
|
||||
{"44'/0'/1'/0"}, // too few fields
|
||||
{"44'/0'/0'/2/0"}, // change field can only be 0/1
|
||||
{"44/0'/0'/0/0"}, // first field needs '
|
||||
{"44'/0/0'/0/0"}, // second field needs '
|
||||
{"44'/0'/0/0/0"}, // third field needs '
|
||||
{"44'/0'/0'/0'/0"}, // fourth field must not have '
|
||||
{"44'/0'/0'/0/0'"}, // fifth field must not have '
|
||||
{"44'/-1'/0'/0/0"}, // no negatives
|
||||
{"44'/0'/0'/-1/0"}, // no negatives
|
||||
{"a'/0'/0'/-1/0"}, // valid values
|
||||
{"0/X/0'/-1/0"}, // valid values
|
||||
{"44'/0'/X/-1/0"}, // valid values
|
||||
{"44'/0'/0'/%/0"}, // valid values
|
||||
{"44'/0'/0'/0/%"}, // valid values
|
||||
{"m/43'/0'/0'/0/0"}, // doesn't start with 44
|
||||
{"m/44'/1'/0'/0/0/5"}, // too many fields
|
||||
{"m/44'/0'/1'/0"}, // too few fields
|
||||
{"m/44'/0'/0'/2/0"}, // change field can only be 0/1
|
||||
{"m/44/0'/0'/0/0"}, // first field needs '
|
||||
{"m/44'/0/0'/0/0"}, // second field needs '
|
||||
{"m/44'/0'/0/0/0"}, // third field needs '
|
||||
{"m/44'/0'/0'/0'/0"}, // fourth field must not have '
|
||||
{"m/44'/0'/0'/0/0'"}, // fifth field must not have '
|
||||
{"m/44'/-1'/0'/0/0"}, // no negatives
|
||||
{"m/44'/0'/0'/-1/0"}, // no negatives
|
||||
{"m/a'/0'/0'/-1/0"}, // invalid values
|
||||
{"m/0/X/0'/-1/0"}, // invalid values
|
||||
{"m/44'/0'/X/-1/0"}, // invalid values
|
||||
{"m/44'/0'/0'/%/0"}, // invalid values
|
||||
{"m/44'/0'/0'/0/%"}, // invalid values
|
||||
{"m44'0'0'00"}, // no separators
|
||||
{" /44'/0'/0'/0/0"}, // blank first component
|
||||
}
|
||||
|
||||
for i, c := range badCases {
|
||||
params, err := hd.NewParamsFromPath(c.path)
|
||||
errStr := fmt.Sprintf("%d %v", i, c)
|
||||
assert.Nil(t, params, errStr)
|
||||
assert.Error(t, err, errStr)
|
||||
require.Nil(t, params, errStr)
|
||||
require.Error(t, err, errStr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// nolint:govet
|
||||
func ExampleSomeBIP32TestVecs() {
|
||||
func TestCreateHDPath(t *testing.T) {
|
||||
type args struct {
|
||||
coinType uint32
|
||||
account uint32
|
||||
index uint32
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want hd.BIP44Params
|
||||
}{
|
||||
{"m/44'/0'/0'/0/0", args{0, 0, 0}, hd.BIP44Params{Purpose: 44}},
|
||||
{"m/44'/114'/0'/0/0", args{114, 0, 0}, hd.BIP44Params{Purpose: 44, CoinType: 114, Account: 0, AddressIndex: 0}},
|
||||
{"m/44'/114'/1'/1/0", args{114, 1, 1}, hd.BIP44Params{Purpose: 44, CoinType: 114, Account: 1, AddressIndex: 1}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt := tt
|
||||
require.Equal(t, tt.want, *hd.CreateHDPath(tt.args.coinType, tt.args.account, tt.args.index))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Tests to ensure that any index value is in the range [0, max(int32)] as per
|
||||
// the extended keys specification. If the index belongs to that of a hardened key,
|
||||
// its 0x80000000 bit will be set, so we can still accept values in [0, max(int32)] and then
|
||||
// increase its value as deriveKeyPath already augments.
|
||||
// See issue https://github.com/cosmos/cosmos-sdk/issues/7627.
|
||||
func TestDeriveHDPathRange(t *testing.T) {
|
||||
seed := mnemonicToSeed("I am become Death, the destroyer of worlds!")
|
||||
|
||||
tests := []struct {
|
||||
path string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
path: "m/1'/2147483648/0'/0/0",
|
||||
wantErr: "out of range",
|
||||
},
|
||||
{
|
||||
path: "m/2147483648'/1/0/0",
|
||||
wantErr: "out of range",
|
||||
},
|
||||
{
|
||||
path: "m/2147483648'/2147483648/0'/0/0",
|
||||
wantErr: "out of range",
|
||||
},
|
||||
{
|
||||
path: "m/1'/-5/0'/0/0",
|
||||
wantErr: "invalid syntax",
|
||||
},
|
||||
{
|
||||
path: "m/-2147483646'/1/0/0",
|
||||
wantErr: "invalid syntax",
|
||||
},
|
||||
{
|
||||
path: "m/-2147483648'/-2147483648/0'/0/0",
|
||||
wantErr: "invalid syntax",
|
||||
},
|
||||
{
|
||||
path: "m44'118'0'00",
|
||||
wantErr: "path 'm44'118'0'00' doesn't contain '/' separators",
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
wantErr: "path '' doesn't contain '/' separators",
|
||||
},
|
||||
{
|
||||
// Should pass.
|
||||
path: "m/1'/2147483647'/1/0'/0/0",
|
||||
},
|
||||
{
|
||||
// Should pass.
|
||||
path: "1'/2147483647'/1/0'/0/0",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.path, func(t *testing.T) {
|
||||
master, ch := hd.ComputeMastersFromSeed(seed)
|
||||
_, err := hd.DerivePrivateKeyForPath(master, ch, tt.path)
|
||||
|
||||
if tt.wantErr == "" {
|
||||
require.NoError(t, err, "unexpected error")
|
||||
} else {
|
||||
require.Error(t, err, "expected a report of an int overflow")
|
||||
require.Contains(t, err.Error(), tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleStringifyPathParams() {
|
||||
path := hd.NewParams(44, 0, 0, false, 0)
|
||||
fmt.Println(path.String())
|
||||
path = hd.NewParams(44, 33, 7, true, 9)
|
||||
fmt.Println(path.String())
|
||||
// Output:
|
||||
// m/44'/0'/0'/0/0
|
||||
// m/44'/33'/7'/1/9
|
||||
}
|
||||
|
||||
func ExampleSomeBIP32TestVecs() {
|
||||
seed := mnemonicToSeed("barrel original fuel morning among eternal " +
|
||||
"filter ball stove pluck matrix mechanic")
|
||||
master, ch := hd.ComputeMastersFromSeed(seed)
|
||||
@ -189,89 +281,3 @@ func ExampleSomeBIP32TestVecs() {
|
||||
//
|
||||
// c4c11d8c03625515905d7e89d25dfc66126fbc629ecca6db489a1a72fc4bda78
|
||||
}
|
||||
|
||||
func TestCreateHDPath(t *testing.T) {
|
||||
type args struct {
|
||||
coinType uint32
|
||||
account uint32
|
||||
index uint32
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want hd.BIP44Params
|
||||
}{
|
||||
{"44'/0'/0'/0/0", args{0, 0, 0}, hd.BIP44Params{Purpose: 44}},
|
||||
{"44'/114'/0'/0/0", args{114, 0, 0}, hd.BIP44Params{Purpose: 44, CoinType: 114, Account: 0, AddressIndex: 0}},
|
||||
{"44'/114'/1'/1/0", args{114, 1, 1}, hd.BIP44Params{Purpose: 44, CoinType: 114, Account: 1, AddressIndex: 1}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt := tt
|
||||
require.Equal(t, tt.want, *hd.CreateHDPath(tt.args.coinType, tt.args.account, tt.args.index))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Tests to ensure that any index value is in the range [0, max(int32)] as per
|
||||
// the extended keys specification. If the index belongs to that of a hardened key,
|
||||
// its 0x80000000 bit will be set, so we can still accept values in [0, max(int32)] and then
|
||||
// increase its value as deriveKeyPath already augments.
|
||||
// See issue https://github.com/cosmos/cosmos-sdk/issues/7627.
|
||||
func TestDeriveHDPathRange(t *testing.T) {
|
||||
seed := mnemonicToSeed("I am become Death, the destroyer of worlds!")
|
||||
|
||||
tests := []struct {
|
||||
path string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
path: "1'/2147483648/0'/0/0",
|
||||
wantErr: "out of range",
|
||||
},
|
||||
{
|
||||
path: "2147483648'/1/0/0",
|
||||
wantErr: "out of range",
|
||||
},
|
||||
{
|
||||
path: "2147483648'/2147483648/0'/0/0",
|
||||
wantErr: "out of range",
|
||||
},
|
||||
{
|
||||
path: "1'/-5/0'/0/0",
|
||||
wantErr: "invalid syntax",
|
||||
},
|
||||
{
|
||||
path: "-2147483646'/1/0/0",
|
||||
wantErr: "invalid syntax",
|
||||
},
|
||||
{
|
||||
path: "-2147483648'/-2147483648/0'/0/0",
|
||||
wantErr: "invalid syntax",
|
||||
},
|
||||
{
|
||||
// Should pass.
|
||||
path: "1'/2147483647/0'/0/0",
|
||||
},
|
||||
{
|
||||
// Should pass.
|
||||
path: "2147483647'/1/0'/0/0",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.path, func(t *testing.T) {
|
||||
master, ch := hd.ComputeMastersFromSeed(seed)
|
||||
_, err := hd.DerivePrivateKeyForPath(master, ch, tt.path)
|
||||
|
||||
if tt.wantErr == "" {
|
||||
require.Nil(t, err, "unexpected error")
|
||||
} else {
|
||||
require.NotNil(t, err, "expected a report of an int overflow")
|
||||
require.Contains(t, err.Error(), tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ func TestInMemoryCreateLedger(t *testing.T) {
|
||||
|
||||
path, err := restoredKey.GetPath()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "44'/118'/3'/0/1", path.String())
|
||||
require.Equal(t, "m/44'/118'/3'/0/1", path.String())
|
||||
}
|
||||
|
||||
// TestSignVerify does some detailed checks on how we sign and validate
|
||||
@ -123,5 +123,5 @@ func TestAltKeyring_SaveLedgerKey(t *testing.T) {
|
||||
|
||||
path, err := restoredKey.GetPath()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "44'/118'/3'/0/1", path.String())
|
||||
require.Equal(t, "m/44'/118'/3'/0/1", path.String())
|
||||
}
|
||||
|
||||
@ -4,11 +4,10 @@ import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_writeReadLedgerInfo(t *testing.T) {
|
||||
@ -17,28 +16,27 @@ func Test_writeReadLedgerInfo(t *testing.T) {
|
||||
copy(tmpKey[:], bz)
|
||||
|
||||
lInfo := newLedgerInfo("some_name", &secp256k1.PubKey{Key: tmpKey}, *hd.NewFundraiserParams(5, sdk.CoinType, 1), hd.Secp256k1Type)
|
||||
assert.Equal(t, TypeLedger, lInfo.GetType())
|
||||
require.Equal(t, TypeLedger, lInfo.GetType())
|
||||
|
||||
path, err := lInfo.GetPath()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "44'/118'/5'/0/1", path.String())
|
||||
assert.Equal(t,
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "m/44'/118'/5'/0/1", path.String())
|
||||
require.Equal(t,
|
||||
"cosmospub1addwnpepqddddqg2glc8x4fl7vxjlnr7p5a3czm5kcdp4239sg6yqdc4rc2r5wmxv8p",
|
||||
sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, lInfo.GetPubKey()))
|
||||
|
||||
// Serialize and restore
|
||||
serialized := marshalInfo(lInfo)
|
||||
restoredInfo, err := unmarshalInfo(serialized)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, restoredInfo)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, restoredInfo)
|
||||
|
||||
// Check both keys match
|
||||
assert.Equal(t, lInfo.GetName(), restoredInfo.GetName())
|
||||
assert.Equal(t, lInfo.GetType(), restoredInfo.GetType())
|
||||
assert.Equal(t, lInfo.GetPubKey(), restoredInfo.GetPubKey())
|
||||
require.Equal(t, lInfo.GetName(), restoredInfo.GetName())
|
||||
require.Equal(t, lInfo.GetType(), restoredInfo.GetType())
|
||||
require.Equal(t, lInfo.GetPubKey(), restoredInfo.GetPubKey())
|
||||
|
||||
restoredPath, err := restoredInfo.GetPath()
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, path, restoredPath)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, path, restoredPath)
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ func TestErrorHandling(t *testing.T) {
|
||||
func TestPublicKeyUnsafe(t *testing.T) {
|
||||
path := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
|
||||
priv, err := NewPrivKeySecp256k1Unsafe(path)
|
||||
require.Nil(t, err, "%s", err)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, priv)
|
||||
|
||||
require.Equal(t, "eb5ae98721034fef9cd7c4c63588d3b03feb5281b9d232cba34d6f3d71aee59211ffbfe1fe87",
|
||||
@ -62,10 +62,10 @@ func TestPublicKeyUnsafeHDPath(t *testing.T) {
|
||||
// Check with device
|
||||
for i := uint32(0); i < 10; i++ {
|
||||
path := *hd.NewFundraiserParams(0, sdk.CoinType, i)
|
||||
fmt.Printf("Checking keys at %v\n", path)
|
||||
t.Logf("Checking keys at %v\n", path)
|
||||
|
||||
priv, err := NewPrivKeySecp256k1Unsafe(path)
|
||||
require.Nil(t, err, "%s", err)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, priv)
|
||||
|
||||
// Check other methods
|
||||
@ -100,7 +100,7 @@ func TestPublicKeySafe(t *testing.T) {
|
||||
path := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
|
||||
priv, addr, err := NewPrivKeySecp256k1(path, "cosmos")
|
||||
|
||||
require.Nil(t, err, "%s", err)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, priv)
|
||||
|
||||
require.Nil(t, ShowAddress(path, priv.PubKey(), sdk.GetConfig().GetBech32AccountAddrPrefix()))
|
||||
@ -155,10 +155,10 @@ func TestPublicKeyHDPath(t *testing.T) {
|
||||
// Check with device
|
||||
for i := uint32(0); i < 10; i++ {
|
||||
path := *hd.NewFundraiserParams(0, sdk.CoinType, i)
|
||||
fmt.Printf("Checking keys at %v\n", path)
|
||||
t.Logf("Checking keys at %s\n", path)
|
||||
|
||||
priv, addr, err := NewPrivKeySecp256k1(path, "cosmos")
|
||||
require.Nil(t, err, "%s", err)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, addr)
|
||||
require.NotNil(t, priv)
|
||||
|
||||
@ -209,14 +209,14 @@ func TestSignaturesHD(t *testing.T) {
|
||||
msg := getFakeTx(account)
|
||||
|
||||
path := *hd.NewFundraiserParams(account, sdk.CoinType, account/5)
|
||||
fmt.Printf("Checking signature at %v --- PLEASE REVIEW AND ACCEPT IN THE DEVICE\n", path)
|
||||
t.Logf("Checking signature at %v --- PLEASE REVIEW AND ACCEPT IN THE DEVICE\n", path)
|
||||
|
||||
priv, err := NewPrivKeySecp256k1Unsafe(path)
|
||||
require.Nil(t, err, "%s", err)
|
||||
require.NoError(t, err)
|
||||
|
||||
pub := priv.PubKey()
|
||||
sig, err := priv.Sign(msg)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
valid := pub.VerifySignature(msg, sig)
|
||||
require.True(t, valid, "Is your device using test mnemonic: %s ?", testutil.TestMnemonic)
|
||||
@ -227,11 +227,11 @@ func TestRealDeviceSecp256k1(t *testing.T) {
|
||||
msg := getFakeTx(50)
|
||||
path := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
|
||||
priv, err := NewPrivKeySecp256k1Unsafe(path)
|
||||
require.Nil(t, err, "%s", err)
|
||||
require.NoError(t, err)
|
||||
|
||||
pub := priv.PubKey()
|
||||
sig, err := priv.Sign(msg)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
valid := pub.VerifySignature(msg, sig)
|
||||
require.True(t, valid)
|
||||
@ -246,7 +246,7 @@ func TestRealDeviceSecp256k1(t *testing.T) {
|
||||
|
||||
// signing with the loaded key should match the original pubkey
|
||||
sig, err = priv.Sign(msg)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
valid = pub.VerifySignature(msg, sig)
|
||||
require.True(t, valid)
|
||||
|
||||
|
||||
@ -43,18 +43,21 @@ Read about the [PROCESS](./PROCESS.md).
|
||||
|
||||
- [ADR 001: Coin Source Tracing](./adr-001-coin-source-tracing.md)
|
||||
- [ADR 002: SDK Documentation Structure](./adr-002-docs-structure.md)
|
||||
- [ADR 004: Split Denomination Keys](./adr-004-split-denomination-keys.md)
|
||||
- [ADR 006: Secret Store Replacement](./adr-006-secret-store-replacement.md)
|
||||
- [ADR 009: Evidence Module](./adr-009-evidence-module.md)
|
||||
- [ADR 010: Modular AnteHandler](./adr-010-modular-antehandler.md)
|
||||
- [ADR 019: Protocol Buffer State Encoding](./adr-019-protobuf-state-encoding.md)
|
||||
- [ADR 020: Protocol Buffer Transaction Encoding](./adr-020-protobuf-transaction-encoding.md)
|
||||
- [ADR 021: Protocol Buffer Query Encoding](./adr-021-protobuf-query-encoding.md)
|
||||
- [ADR 023: Protocol Buffer Naming and Versioning](./adr-023-protobuf-naming.md)
|
||||
- [ADR 026: IBC Client Recovery Mechanisms](./adr-026-ibc-client-recovery-mechanisms.md)
|
||||
- [ADR 029: Fee Grant Module](./adr-029-fee-grant-module.md)
|
||||
- [ADR 031: Protobuf Msg Services](./adr-031-msg-service.md)
|
||||
|
||||
### Proposed
|
||||
|
||||
- [ADR 003: Dynamic Capability Store](./adr-003-dynamic-capability-store.md)
|
||||
- [ADR 004: Split Denomination Keys](./adr-004-split-denomination-keys.md)
|
||||
- [ADR 011: Generalize Genesis Accounts](./adr-011-generalize-genesis-accounts.md)
|
||||
- [ADR 012: State Accessors](./adr-012-state-accessors.md)
|
||||
- [ADR 013: Metrics](./adr-013-metrics.md)
|
||||
@ -62,14 +65,11 @@ Read about the [PROCESS](./PROCESS.md).
|
||||
- [ADR 016: Validator Consensus Key Rotation](./adr-016-validator-consensus-key-rotation.md)
|
||||
- [ADR 017: Historical Header Module](./adr-017-historical-header-module.md)
|
||||
- [ADR 018: Extendable Voting Periods](./adr-018-extendable-voting-period.md)
|
||||
- [ADR 021: Protocol Buffer Query Encoding](./adr-021-protobuf-query-encoding.md)
|
||||
- [ADR 022: Custom baseapp panic handling](./adr-022-custom-panic-handling.md)
|
||||
- [ADR 023: Protocol Buffer Naming and Versioning](./adr-023-protobuf-naming.md)
|
||||
- [ADR 024: Coin Metadata](./adr-024-coin-metadata.md)
|
||||
- [ADR 025: IBC Passive Channels](./adr-025-ibc-passive-channels.md)
|
||||
- [ADR 027: Deterministic Protobuf Serialization](./adr-027-deterministic-protobuf-serialization.md)
|
||||
- [ADR 028: Public Key Addresses](./adr-028-public-key-addresses.md)
|
||||
- [ADR 031: Protobuf Msg Services](./adr-031-msg-service.md)
|
||||
- [ADR 032: Typed Events](./adr-032-typed-events.md)
|
||||
- [ADR 035: Rosetta API Support](./adr-035-rosetta-api-support.md)
|
||||
- [ADR 037: Governance Split Votes](./adr-037-gov-split-vote.md)
|
||||
@ -96,7 +96,7 @@ the balances and check that they match the expected total supply.
|
||||
|
||||
## Status
|
||||
|
||||
Proposed.
|
||||
Accepted.
|
||||
|
||||
## Consequences
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
Accepted
|
||||
|
||||
## Abstract
|
||||
|
||||
@ -53,7 +53,7 @@ This isn't necessarily bad, but it does add overhead to creating modules.
|
||||
We decide to use protobuf `service` definitions for defining `Msg`s as well as
|
||||
the code generated by them as a replacement for `Msg` handlers.
|
||||
|
||||
Below we define how this will look for the `SubmitProposal` message from `x/gov` module.
|
||||
Below we define how this will look for the `SubmitProposal` message from `x/gov` module.
|
||||
We start with a `Msg` `service` definition:
|
||||
|
||||
```proto
|
||||
@ -105,7 +105,7 @@ should use the more canonical `Msg...Request` names.
|
||||
Currently, we are encoding `Msg`s as `Any` in `Tx`s which involves packing the
|
||||
binary-encoded `Msg` with its type URL.
|
||||
|
||||
The type URL for `MsgSubmitProposal` based on the proto3 spec is `/cosmos.gov.MsgSubmitProposal`.
|
||||
The type URL for `MsgSubmitProposal` based on the proto3 spec is `/cosmos.gov.MsgSubmitProposal`.
|
||||
|
||||
The fully-qualified name for the `SubmitProposal` service method above (also
|
||||
based on the proto3 and gRPC specs) is `/cosmos.gov.Msg/SubmitProposal` which varies
|
||||
@ -117,7 +117,7 @@ In order to encode service methods in transactions, we encode them as `Any`s in
|
||||
the same `TxBody.messages` field as other `Msg`s. We simply set `Any.type_url`
|
||||
to the full-qualified method name (ex. `/cosmos.gov.Msg/SubmitProposal`) and
|
||||
set `Any.value` to the protobuf encoding of the request message
|
||||
(`MsgSubmitProposal` in this case).
|
||||
(`MsgSubmitProposal` in this case).
|
||||
|
||||
### Decoding
|
||||
|
||||
@ -125,7 +125,7 @@ When decoding, `TxBody.UnpackInterfaces` will need a special case
|
||||
to detect if `Any` type URLs match the service method format (ex. `/cosmos.gov.Msg/SubmitProposal`)
|
||||
by checking for two `/` characters. Messages that are method names plus request parameters
|
||||
instead of a normal `Any` messages will get unpacked into the `ServiceMsg` struct:
|
||||
|
||||
|
||||
```go
|
||||
type ServiceMsg struct {
|
||||
// MethodName is the fully-qualified service name
|
||||
@ -139,7 +139,7 @@ type ServiceMsg struct {
|
||||
|
||||
In the future, `service` definitions may become the primary method for defining
|
||||
`Msg`s. As a starting point, we need to integrate with the SDK's existing routing
|
||||
and `Msg` interface.
|
||||
and `Msg` interface.
|
||||
|
||||
To do this, `ServiceMsg` implements the `sdk.Msg` interface and its handler does the
|
||||
actual method routing, allowing this feature to be added incrementally on top of
|
||||
@ -218,17 +218,25 @@ Separate handler definition is no longer needed with this approach.
|
||||
|
||||
## Consequences
|
||||
|
||||
This design changes how a module functionality is exposed and accessed. It deprecates the existing `Handler` interface and `AppModule.Route` in favor of [Protocol Buffer Services](https://developers.google.com/protocol-buffers/docs/proto3#services) and Service Routing described above. This dramatically simplifies the code. We don't need to create handlers and keepers any more. Use of Protocol Buffer auto-generated clients clearly separates the communication interfaces between the module and a modules user. The control logic (aka handlers and keepers) is not exposed any more. A module interface can be seen as a black box accessible through a client API. It's worth to note that the client interfaces are also generated by Protocol Buffers.
|
||||
|
||||
This also allows us to change how we perform functional tests. Instead of mocking AppModules and Router, we will mock a client (server will stay hidden). More specifically: we will never mock `moduleA.MsgServer` in `moduleB`, but rather `moduleA.MsgClient`. One can think about it as working with external services (eg DBs, or online servers...). We assume that the transmission between clients and servers is correctly handled by generated Protocol Buffers.
|
||||
|
||||
Finally, closing a module to client API opens desirable OCAP patterns discussed in ADR-033. Since server implementation and interface is hidden, nobody can hold "keepers"/servers and will be forced to relay on the client interface, which will drive developers for correct encapsulation and software engineering patterns.
|
||||
|
||||
### Pros
|
||||
- communicates return type clearly
|
||||
- manual handler registration and return type marshaling is no longer needed, just implement the interface and register it
|
||||
- some keeper code could be automatically generate, this would improve the UX of [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) approach (1) if we chose to adopt that
|
||||
- generated client code could be useful for clients
|
||||
- communication interface is automatically generated, the developer can now focus only on the state transition methods - this would improve the UX of [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) approach (1) if we chose to adopt that
|
||||
- generated client code could be useful for clients and tests
|
||||
- dramatically reduces and simplifies the code
|
||||
|
||||
### Cons
|
||||
- supporting both this and the current concrete `Msg` type approach simultaneously could be confusing
|
||||
(we could choose to deprecate the current approach)
|
||||
- using `service` definitions outside the context of gRPC could be confusing (but doesn’t violate the proto3 spec)
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- [Initial Github Issue \#7122](https://github.com/cosmos/cosmos-sdk/issues/7122)
|
||||
|
||||
@ -158,7 +158,7 @@ Each module should also implement the `RegisterServices` method as part of the [
|
||||
|
||||
#### Handlers
|
||||
|
||||
The [handler](../building-modules/handler.md) refers to the part of the module responsible for processing the `Msg` after it is routed by `baseapp`. Handler functions of modules are only executed if the transaction is relayed from Tendermint by the `DeliverTx` ABCI message. If the transaction is relayed by `CheckTx`, only stateless checks and fee-related stateful checks are performed. To better understand the difference between `DeliverTx`and `CheckTx`, as well as the difference between stateful and stateless checks, click [here](./tx-lifecycle.md).
|
||||
The [handler](../building-modules/msg-services.md#handler-type) refers to the part of the module responsible for processing the `Msg` after it is routed by `baseapp`. Handler functions of modules are only executed if the transaction is relayed from Tendermint by the `DeliverTx` ABCI message. If the transaction is relayed by `CheckTx`, only stateless checks and fee-related stateful checks are performed. To better understand the difference between `DeliverTx`and `CheckTx`, as well as the difference between stateful and stateless checks, click [here](./tx-lifecycle.md).
|
||||
|
||||
The `handler` of a module is generally defined in a file called `handler.go` and consists of:
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ By default, the Cosmos SDK makes use of two different gas meters, the [main gas
|
||||
|
||||
`ctx.GasMeter()` is the main gas meter of the application. The main gas meter is initialized in `BeginBlock` via `setDeliverState`, and then tracks gas consumption during execution sequences that lead to state-transitions, i.e. those originally triggered by [`BeginBlock`](../core/baseapp.md#beginblock), [`DeliverTx`](../core/baseapp.md#delivertx) and [`EndBlock`](../core/baseapp.md#endblock). At the beginning of each `DeliverTx`, the main gas meter **must be set to 0** in the [`AnteHandler`](#antehandler), so that it can track gas consumption per-transaction.
|
||||
|
||||
Gas consumption can be done manually, generally by the module developer in the [`BeginBlocker`, `EndBlocker`](../building-modules/beginblock-endblock.md) or [`handler`](../building-modules/handler.md), but most of the time it is done automatically whenever there is a read or write to the store. This automatic gas consumption logic is implemented in a special store called [`GasKv`](../core/store.md#gaskv-store).
|
||||
Gas consumption can be done manually, generally by the module developer in the [`BeginBlocker`, `EndBlocker`](../building-modules/beginblock-endblock.md) or [`Msg` service](../building-modules/msg-services.md), but most of the time it is done automatically whenever there is a read or write to the store. This automatic gas consumption logic is implemented in a special store called [`GasKv`](../core/store.md#gaskv-store).
|
||||
|
||||
### Block Gas Meter
|
||||
|
||||
|
||||
@ -179,7 +179,7 @@ explicitly ordered in the block proposal.
|
||||
|
||||
### DeliverTx
|
||||
|
||||
The `DeliverTx` ABCI function defined in [`baseapp`](../core/baseapp.md) does the bulk of the
|
||||
The `DeliverTx` ABCI function defined in [`BaseApp`](../core/baseapp.md) does the bulk of the
|
||||
state transitions: it is run for each transaction in the block in sequential order as committed
|
||||
to during consensus. Under the hood, `DeliverTx` is almost identical to `CheckTx` but calls the
|
||||
[`runTx`](../core/baseapp.md#runtx) function in deliver mode instead of check mode.
|
||||
@ -194,15 +194,15 @@ Instead of using their `checkState`, full-nodes use `deliverState`:
|
||||
`AnteHandler` will not compare `gas-prices` to the node's `min-gas-prices` since that value is local
|
||||
to each node - differing values across nodes would yield nondeterministic results.
|
||||
|
||||
- **Route and Handler:** While `CheckTx` would have exited, `DeliverTx` continues to run
|
||||
- **`MsgServiceRouter`:** While `CheckTx` would have exited, `DeliverTx` continues to run
|
||||
[`runMsgs`](../core/baseapp.md#runtx-and-runmsgs) to fully execute each `Msg` within the transaction.
|
||||
Since the transaction may have messages from different modules, `baseapp` needs to know which module
|
||||
to find the appropriate Handler. Thus, the `route` function is called via the [module manager](../building-modules/module-manager.md) to
|
||||
retrieve the route name and find the [`Handler`](../building-modules/handler.md) within the module.
|
||||
Since the transaction may have messages from different modules, `BaseApp` needs to know which module
|
||||
to find the appropriate handler. This is achieved using `BaseApp`'s `MsgServiceRouter` so that it can be processed by the module's [`Msg` service](../building-modules/msg-services.md).
|
||||
For legacy `Msg` routing, the `Route` function is called via the [module manager](../building-modules/module-manager.md) to retrieve the route name and find the legacy [`Handler`](../building-modules/msg-services.md#handler-type) within the module.
|
||||
|
||||
- **Handler:** The `handler`, a step up from `AnteHandler`, is responsible for executing each
|
||||
- **`Msg` service:** The `Msg` service, a step up from `AnteHandler`, is responsible for executing each
|
||||
message in the `Tx` and causes state transitions to persist in `deliverTxState`. It is defined
|
||||
within a `Msg`'s module and writes to the appropriate stores within the module.
|
||||
within a module `Msg` protobuf service and writes to the appropriate stores within the module.
|
||||
|
||||
- **Gas:** While a `Tx` is being delivered, a `GasMeter` is used to keep track of how much
|
||||
gas is being used; if execution completes, `GasUsed` is set and returned in the
|
||||
|
||||
@ -11,7 +11,7 @@ This repository contains documentation on concepts developers need to know in or
|
||||
1. [Introduction to Cosmos SDK Modules](./intro.md)
|
||||
2. [`AppModule` Interface and Module Manager](./module-manager.md)
|
||||
3. [Messages and Queries](./messages-and-queries.md)
|
||||
4. [`Handler`s - Processing Messages](./handler.md)
|
||||
4. [`Msg` services - Processing Messages](./msg-services.md)
|
||||
5. [Query Services - Processing Queries](./query-services.md)
|
||||
6. [BeginBlocker and EndBlocker](./beginblock-endblock.md)
|
||||
7. [`Keeper`s](./keeper.md)
|
||||
|
||||
@ -16,7 +16,7 @@ order: 6
|
||||
|
||||
When needed, `BeginBlocker` and `EndBlocker` are implemented as part of the [`AppModule` interface](./module-manager.md#appmodule). The `BeginBlock` and `EndBlock` methods of the interface implemented in `module.go` generally defer to `BeginBlocker` and `EndBlocker` methods respectively, which are usually implemented in a **`abci.go`** file.
|
||||
|
||||
The actual implementation of `BeginBlocker` and `EndBlocker` in `./abci.go` are very similar to that of a [`handler`](./handler.md):
|
||||
The actual implementation of `BeginBlocker` and `EndBlocker` in `./abci.go` are very similar to that of a [`Msg` service](./msg-services.md):
|
||||
|
||||
- They generally use the [`keeper`](./keeper.md) and [`ctx`](../core/context.md) to retrieve information about the latest state.
|
||||
- If needed, they use the `keeper` and `ctx` to trigger state-transitions.
|
||||
|
||||
@ -1,96 +0,0 @@
|
||||
<!--
|
||||
order: 4
|
||||
-->
|
||||
|
||||
# Handlers
|
||||
|
||||
A `Handler` designates a function that processes [`message`s](./messages-and-queries.md#messages). `Handler`s are specific to the module in which they are defined, and only process `message`s defined within the said module. They are called from `baseapp` during [`DeliverTx`](../core/baseapp.md#delivertx). {synopsis}
|
||||
|
||||
## Pre-requisite Readings
|
||||
|
||||
- [Module Manager](./module-manager.md) {prereq}
|
||||
- [Messages and Queries](./messages-and-queries.md) {prereq}
|
||||
|
||||
## `handler` type
|
||||
|
||||
The `handler` type defined in the Cosmos SDK specifies the typical structure of a `handler` function.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/handler.go#L4
|
||||
|
||||
Let us break it down:
|
||||
|
||||
- The [`Msg`](./messages-and-queries.md#messages) is the actual object being processed.
|
||||
- The [`Context`](../core/context.md) contains all the necessary information needed to process the `msg`, as well as a cache-wrapped copy of the latest state. If the `msg` is succesfully processed, the modified version of the temporary state contained in the `ctx` will be written to the main state.
|
||||
- The [`*Result`] returned to `baseapp`, which contains (among other things) information on the execution of the `handler` and [`events`](../core/events.md).
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/base/abci/v1beta1/abci.proto#L81-L95
|
||||
|
||||
## Implementation of a module `handler`s
|
||||
|
||||
Module `handler`s are typically implemented in a `./handler.go` file inside the module's folder. The
|
||||
[module manager](./module-manager.md) is used to add the module's `handler`s to the
|
||||
[application's `router`](../core/baseapp.md#message-routing) via the `Route()` method. Typically,
|
||||
the manager's `Route()` method simply constructs a Route that calls a `NewHandler()` method defined in `handler.go`,
|
||||
which looks like the following:
|
||||
|
||||
```go
|
||||
func NewHandler(keeper Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
||||
switch msg := msg.(type) {
|
||||
case *MsgType1:
|
||||
return handleMsgType1(ctx, keeper, msg)
|
||||
|
||||
case *MsgType2:
|
||||
return handleMsgType2(ctx, keeper, msg)
|
||||
|
||||
default:
|
||||
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
First, the `handler` function sets a new `EventManager` to the context to isolate events per `msg`.
|
||||
Then, this simple switch returns a `handler` function specific to the type of the received `message`. These `handler` functions are the ones that actually process `message`s, and usually follow the following 2 steps:
|
||||
|
||||
- First, they perform *stateful* checks to make sure the `message` is valid. At this stage, the `message`'s `ValidateBasic()` method has already been called, meaning *stateless* checks on the message (like making sure parameters are correctly formatted) have already been performed. Checks performed in the `handler` can be more expensive and require access to the state. For example, a `handler` for a `transfer` message might check that the sending account has enough funds to actually perform the transfer. To access the state, the `handler` needs to call the [`keeper`'s](./keeper.md) getter functions.
|
||||
- Then, if the checks are successfull, the `handler` calls the [`keeper`'s](./keeper.md) setter functions to actually perform the state transition.
|
||||
|
||||
Before returning, `handler` functions generally emit one or multiple [`events`](../core/events.md) via the `EventManager` held in the `ctx`:
|
||||
|
||||
```go
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
eventType, // e.g. sdk.EventTypeMessage for a message, types.CustomEventType for a custom event defined in the module
|
||||
sdk.NewAttribute(attributeKey, attributeValue),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
These `events` are relayed back to the underlying consensus engine and can be used by service providers to implement services around the application. Click [here](../core/events.md) to learn more about `events`.
|
||||
|
||||
Finally, the `handler` function returns a `*sdk.Result` which contains the aforementioned `events` and an optional `Data` field.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/base/abci/v1beta1/abci.proto#L81-L95
|
||||
|
||||
Next is an example of how to return a `*Result` from the `gov` module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/gov/handler.go#L67-L70
|
||||
|
||||
For a deeper look at `handler`s, see this [example implementation of a `handler` function](https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/gov/handler.go) from the `gov` module.
|
||||
|
||||
The `handler` can then be registered from [`AppModule.Route()`](./module-manager.md#appmodule) as shown in the example below:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/228728cce2af8d494c8b4e996d011492139b04ab/x/gov/module.go#L143-L146
|
||||
|
||||
## Telemetry
|
||||
|
||||
New [telemetry metrics](../core/telemetry.md) can be created from the `handler` when handling messages for instance.
|
||||
|
||||
This is an example from the `auth` module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/auth/vesting/handler.go#L68-L80
|
||||
|
||||
## Next {hide}
|
||||
|
||||
Learn about [query services](./query-services.md) {hide}
|
||||
@ -17,7 +17,7 @@ The Cosmos SDK can be thought as the Ruby-on-Rails of blockchain development. It
|
||||
|
||||
On top of this core, the Cosmos SDK enables developers to build modules that implement the business logic of their application. In other words, SDK modules implement the bulk of the logic of applications, while the core does the wiring and enables modules to be composed together. The end goal is to build a robust ecosystem of open-source SDK modules, making it increasingly easier to build complex blockchain applications.
|
||||
|
||||
SDK Modules can be seen as little state-machines within the state-machine. They generally define a subset of the state using one ore multiple `KVStore` in the [main multistore](../core/store.md), as well as a subset of [`message` types](./messages-and-queries.md#messages). These `message`s are routed by one of the main component of SDK core, [`baseapp`](../core/baseapp.md), to the [`handler`](./handler.md) of the module that define them.
|
||||
SDK Modules can be seen as little state-machines within the state-machine. They generally define a subset of the state using one or more `KVStore`s in the [main multistore](../core/store.md), as well as a subset of [message types](./messages-and-queries.md#messages). These messages are routed by one of the main component of SDK core, [`BaseApp`](../core/baseapp.md), to the [`Msg` service](./msg-services.md) of the module that define them.
|
||||
|
||||
```
|
||||
+
|
||||
@ -76,12 +76,11 @@ While there is no definitive guidelines for writing modules, here are some impor
|
||||
|
||||
## Main Components of SDK Modules
|
||||
|
||||
Modules are by convention defined in the `.x/` subfolder (e.g. the `bank` module will be defined in the `./x/bank` folder). They generally share the same core components:
|
||||
Modules are by convention defined in the `./x/` subfolder (e.g. the `bank` module will be defined in the `./x/bank` folder). They generally share the same core components:
|
||||
|
||||
- Custom [`message` types](./messages-and-queries.md#messages) to trigger state-transitions.
|
||||
- A [`handler`](./handler.md) used to process messages when they are routed to the module by [`baseapp`](../core/baseapp.md#message-routing).
|
||||
- A [`keeper`](./keeper.md), used to access the module's store(s) and update the state.
|
||||
- A [query service](./query-services.md), used to process user queries when they are routed to the module by [`baseapp`](../core/baseapp.md#query-routing).
|
||||
- A [`keeper`](./keeper.md), used to access the module's store(s) and update the state.
|
||||
- A [`Msg` service](./messages-and-queries.md#messages) used to process messages when they are routed to the module by [`BaseApp`](../core/baseapp.md#message-routing) and trigger state-transitions.
|
||||
- A [query service](./query-services.md), used to process user queries when they are routed to the module by [`BaseApp`](../core/baseapp.md#query-routing).
|
||||
- Interfaces, for end users to query the subset of the state defined by the module and create `message`s of the custom types defined in the module.
|
||||
|
||||
In addition to these components, modules implement the `AppModule` interface in order to be managed by the [`module manager`](./module-manager.md).
|
||||
|
||||
@ -46,7 +46,7 @@ Of course, it is possible to define different types of internal `keeper`s for th
|
||||
|
||||
## Implementing Methods
|
||||
|
||||
`Keeper`s primarily expose getter and setter methods for the store(s) managed by their module. These methods should remain as simple as possible and strictly be limited to getting or setting the requested value, as validity checks should have already been performed via the `ValidateBasic()` method of the [`message`](./messages-and-queries.md#messages) and the [`handler`](./handler.md) when `keeper`s' methods are called.
|
||||
`Keeper`s primarily expose getter and setter methods for the store(s) managed by their module. These methods should remain as simple as possible and strictly be limited to getting or setting the requested value, as validity checks should have already been performed via the `ValidateBasic()` method of the [`message`](./messages-and-queries.md#messages) and the [`Msg` server](./msg-services.md) when `keeper`s' methods are called.
|
||||
|
||||
Typically, a *getter* method will present with the following signature
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ order: 3
|
||||
|
||||
# Messages and Queries
|
||||
|
||||
`Message`s and `Queries` are the two primary objects handled by modules. Most of the core components defined in a module, like `handler`s, `keeper`s and `querier`s, exist to process `message`s and `queries`. {synopsis}
|
||||
`Msg`s and `Queries` are the two primary objects handled by modules. Most of the core components defined in a module, like `Msg` services, `keeper`s and `Query` services, exist to process `message`s and `queries`. {synopsis}
|
||||
|
||||
## Pre-requisite Readings
|
||||
|
||||
@ -12,15 +12,36 @@ order: 3
|
||||
|
||||
## Messages
|
||||
|
||||
`Message`s are objects whose end-goal is to trigger state-transitions. They are wrapped in [transactions](../core/transactions.md), which may contain one or multiple of them.
|
||||
`Msg`s are objects whose end-goal is to trigger state-transitions. They are wrapped in [transactions](../core/transactions.md), which may contain one or more of them.
|
||||
|
||||
When a transaction is relayed from the underlying consensus engine to the SDK application, it is first decoded by [`baseapp`](../core/baseapp.md). Then, each `message` contained in the transaction is extracted and routed to the appropriate module via `baseapp`'s `router` so that it can be processed by the module's [`handler`](./handler.md). For a more detailed explanation of the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md).
|
||||
When a transaction is relayed from the underlying consensus engine to the SDK application, it is first decoded by [`BaseApp`](../core/baseapp.md). Then, each message contained in the transaction is extracted and routed to the appropriate module via `BaseApp`'s `MsgServiceRouter` so that it can be processed by the module's [`Msg` service](./msg-services.md). For a more detailed explanation of the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md).
|
||||
|
||||
Defining `message`s is the responsibility of module developers. Typically, they are defined as protobuf messages in a `proto/` directory (see more info about [conventions and naming](../core/encoding.md#faq)). The `message`'s definition usually includes a list of parameters needed to process the message that will be provided by end-users when they want to create a new transaction containing said `message`.
|
||||
### `Msg` Services
|
||||
|
||||
Here's an example of a protobuf message definition:
|
||||
Starting from v0.40, defining Protobuf `Msg` services is the recommended way to handle messages. A `Msg` protobuf service should be created per module, typically in `tx.proto` (see more info about [conventions and naming](../core/encoding.md#faq)). It must have an RPC service method defined for each message in the module.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/gov/v1beta1/tx.proto#L15-L27
|
||||
See an example of a `Msg` service definition from `x/bank` module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/proto/cosmos/bank/v1beta1/tx.proto#L10-L17
|
||||
|
||||
For backwards compatibility with [legacy Amino `Msg`s](#legacy-amino-msgs), existing `Msg` types should be used as the request parameter for `service` definitions. Newer `Msg` types which only support `service` definitions should use the more canonical `Msg...Request` names.
|
||||
|
||||
`Msg` request types need to implement the `MsgRequest` interface which is a simplified version of the `Msg` interface described [below](#legacy-amino-msgs) with only `ValidateBasic()` and `GetSigners()` methods.
|
||||
|
||||
Defining such `Msg` services allow to specify return types as part of `Msg` response using the canonical `Msg...Response` names.
|
||||
|
||||
In addition, this generates client and server code.
|
||||
The generated `MsgServer` interface defines the server API for the `Msg` service and its implementation is described as part of the [`Msg` services](./msg-services.md) documentation.
|
||||
|
||||
A `RegisterMsgServer` method is also generated and should be used to register the module's `MsgServer` implementation in `RegisterServices` method from the [`AppModule` interface](./module-manager.md#appmodule).
|
||||
|
||||
In order for clients (CLI and grpc-gateway) to have these URLs registered, the SDK provides the function `RegisterMsgServiceDesc(registry codectypes.InterfaceRegistry, sd *grpc.ServiceDesc)` that should be called inside module's [`RegisterInterfaces`](module-manager.md#appmodulebasic) method, using the proto-generated `&_Msg_serviceDesc` as `*grpc.ServiceDesc` argument.
|
||||
|
||||
### Legacy Amino `Msg`s
|
||||
|
||||
This way of defining messages is deprecated and using [`Msg` services](#msg-services) is preferred.
|
||||
|
||||
Legacy `Msg`s can be defined as protobuf messages. The messages definition usually includes a list of parameters needed to process the message that will be provided by end-users when they want to create a new transaction containing said message.
|
||||
|
||||
The `Msg` is typically accompanied by a standard constructor function, that is called from one of the [module's interface](./module-interfaces.md). `message`s also need to implement the [`Msg`] interface:
|
||||
|
||||
@ -30,17 +51,17 @@ It extends `proto.Message` and contains the following methods:
|
||||
|
||||
- `Route() string`: Name of the route for this message. Typically all `message`s in a module have the same route, which is most often the module's name.
|
||||
- `Type() string`: Type of the message, used primarly in [events](../core/events.md). This should return a message-specific `string`, typically the denomination of the message itself.
|
||||
- `ValidateBasic() error`: This method is called by `baseapp` very early in the processing of the `message` (in both [`CheckTx`](../core/baseapp.md#checktx) and [`DeliverTx`](../core/baseapp.md#delivertx)), in order to discard obviously invalid messages. `ValidateBasic` should only include *stateless* checks, i.e. checks that do not require access to the state. This usually consists in checking that the message's parameters are correctly formatted and valid (i.e. that the `amount` is strictly positive for a transfer).
|
||||
- `ValidateBasic() error`: This method is called by `BaseApp` very early in the processing of the `message` (in both [`CheckTx`](../core/baseapp.md#checktx) and [`DeliverTx`](../core/baseapp.md#delivertx)), in order to discard obviously invalid messages. `ValidateBasic` should only include *stateless* checks, i.e. checks that do not require access to the state. This usually consists in checking that the message's parameters are correctly formatted and valid (i.e. that the `amount` is strictly positive for a transfer).
|
||||
- `GetSignBytes() []byte`: Return the canonical byte representation of the message. Used to generate a signature.
|
||||
- `GetSigners() []AccAddress`: Return the list of signers. The SDK will make sure that each `message` contained in a transaction is signed by all the signers listed in the list returned by this method.
|
||||
|
||||
See an example implementation of a `message` from the `gov` module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/master/x/gov/types/msgs.go#L94-L136
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/gov/types/msgs.go#L77-L125
|
||||
|
||||
## Queries
|
||||
|
||||
A `query` is a request for information made by end-users of applications through an interface and processed by a full-node. A `query` is received by a full-node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via `baseapp`'s `queryrouter` so that it can be processed by the module's query service (./query-services.md). For a deeper look at the lifecycle of a `query`, click [here](../interfaces/query-lifecycle.md).
|
||||
A `query` is a request for information made by end-users of applications through an interface and processed by a full-node. A `query` is received by a full-node through its consensus engine and relayed to the application via the ABCI. It is then routed to the appropriate module via `BaseApp`'s `queryrouter` so that it can be processed by the module's query service (./query-services.md). For a deeper look at the lifecycle of a `query`, click [here](../interfaces/query-lifecycle.md).
|
||||
|
||||
### gRPC Queries
|
||||
|
||||
@ -52,7 +73,7 @@ Here's an example of such a `Query` service definition:
|
||||
|
||||
As `proto.Message`s, generated `Response` types implement by default `String()` method of [`fmt.Stringer`](https://golang.org/pkg/fmt/#Stringer).
|
||||
|
||||
A `RegisterQueryServer` method is also generated and should be used to register the module's query server in `RegisterQueryService` method from the [`AppModule` interface](./module-manager.md#appmodule).
|
||||
A `RegisterQueryServer` method is also generated and should be used to register the module's query server in the `RegisterServices` method from the [`AppModule` interface](./module-manager.md#appmodule).
|
||||
|
||||
### Legacy Queries
|
||||
|
||||
@ -64,8 +85,8 @@ queryCategory/queryRoute/queryType/arg1/arg2/...
|
||||
|
||||
where:
|
||||
|
||||
- `queryCategory` is the category of the `query`, typically `custom` for module queries. It is used to differentiate between different kinds of queries within `baseapp`'s [`Query` method](../core/baseapp.md#query).
|
||||
- `queryRoute` is used by `baseapp`'s [`queryRouter`](../core/baseapp.md#query-routing) to map the `query` to its module. Usually, `queryRoute` should be the name of the module.
|
||||
- `queryCategory` is the category of the `query`, typically `custom` for module queries. It is used to differentiate between different kinds of queries within `BaseApp`'s [`Query` method](../core/baseapp.md#query).
|
||||
- `queryRoute` is used by `BaseApp`'s [`queryRouter`](../core/baseapp.md#query-routing) to map the `query` to its module. Usually, `queryRoute` should be the name of the module.
|
||||
- `queryType` is used by the module's [`querier`](./query-services.md#legacy-queriers) to map the `query` to the appropriate `querier function` within the module.
|
||||
- `args` are the actual arguments needed to process the `query`. They are filled out by the end-user. Note that for bigger queries, you might prefer passing arguments in the `Data` field of the request `req` instead of the `path`.
|
||||
|
||||
@ -87,4 +108,4 @@ See following examples:
|
||||
|
||||
## Next {hide}
|
||||
|
||||
Learn about [`handler`s](./handler.md) {hide}
|
||||
Learn about [`Msg` services](./msg-services.md) {hide}
|
||||
|
||||
@ -62,17 +62,17 @@ It does not have its own manager, and exists separately from [`AppModule`](#appm
|
||||
|
||||
The `AppModule` interface defines the inter-dependent methods modules need to implement.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/228728cce2af8d494c8b4e996d011492139b04ab/types/module/module.go#L160-L182
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/b4cce159bcc6a32ac78245c6866dd87c73f3720d/types/module/module.go#L160-L182
|
||||
|
||||
`AppModule`s are managed by the [module manager](#manager). This interface embeds the `AppModuleGenesis` interface so that the manager can access all the independent and genesis inter-dependent methods of the module. This means that a concrete type implementing the `AppModule` interface must either implement all the methods of `AppModuleGenesis` (and by extension `AppModuleBasic`), or include a concrete type that does as parameter.
|
||||
|
||||
Let us go through the methods of `AppModule`:
|
||||
|
||||
- `RegisterInvariants(sdk.InvariantRegistry)`: Registers the [`invariants`](./invariants.md) of the module. If the invariants deviates from its predicted value, the [`InvariantRegistry`](./invariants.md#registry) triggers appropriate logic (most often the chain will be halted).
|
||||
- `Route()`: Returns the route for [`message`s](./messages-and-queries.md#messages) to be routed to the module by [`baseapp`](../core/baseapp.md#message-routing).
|
||||
- `QuerierRoute()` (deprecated): Returns the name of the module's query route, for [`queries`](./messages-and-queries.md#queries) to be routes to the module by [`baseapp`](../core/baseapp.md#query-routing).
|
||||
- `Route()`: Returns the route for [`message`s](./messages-and-queries.md#messages) to be routed to the module by [`BaseApp`](../core/baseapp.md#message-routing).
|
||||
- `QuerierRoute()` (deprecated): Returns the name of the module's query route, for [`queries`](./messages-and-queries.md#queries) to be routes to the module by [`BaseApp`](../core/baseapp.md#query-routing).
|
||||
- `LegacyQuerierHandler(*codec.LegacyAmino)` (deprecated): Returns a [`querier`](./query-services.md#legacy-queriers) given the query `path`, in order to process the `query`.
|
||||
- `RegisterQueryService(grpc.Server)`: Allows a module to register a gRPC query service.
|
||||
- `RegisterServices(Configurator)`: Allows a module to register services.
|
||||
- `BeginBlock(sdk.Context, abci.RequestBeginBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. Implement empty if no logic needs to be triggered at the beginning of each block for this module.
|
||||
- `EndBlock(sdk.Context, abci.RequestEndBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. the `staking` module). Implement empty if no logic needs to be triggered at the beginning of each block for this module.
|
||||
|
||||
@ -80,7 +80,7 @@ Let us go through the methods of `AppModule`:
|
||||
|
||||
Typically, the various application module interfaces are implemented in a file called `module.go`, located in the module's folder (e.g. `./x/module/module.go`).
|
||||
|
||||
Almost every module need to implement the `AppModuleBasic` and `AppModule` interfaces. If the module is only used for genesis, it will implement `AppModuleGenesis` instead of `AppModule`. The concrete type that implements the interface can add parameters that are required for the implementation of the various methods of the interface. For example, the `Route()` function often calls a `NewHandler(k keeper)` function defined in [`handler.go`](./handler.md) and therefore needs to pass the module's [`keeper`](./keeper.md) as parameter.
|
||||
Almost every module needs to implement the `AppModuleBasic` and `AppModule` interfaces. If the module is only used for genesis, it will implement `AppModuleGenesis` instead of `AppModule`. The concrete type that implements the interface can add parameters that are required for the implementation of the various methods of the interface. For example, the `Route()` function often calls a `NewHandler(k keeper)` function defined in [`handler.go`](./msg-services.md#handler-type) and therefore needs to pass the module's [`keeper`](./keeper.md) as a parameter.
|
||||
|
||||
```go
|
||||
// example
|
||||
@ -134,12 +134,12 @@ The module manager is used throughout the application whenever an action on a co
|
||||
- `SetOrderBeginBlockers(moduleNames ...string)`: Sets the order in which the `BeginBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
- `SetOrderEndBlockers(moduleNames ...string)`: Sets the order in which the `EndBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
- `RegisterInvariants(ir sdk.InvariantRegistry)`: Registers the [invariants](./invariants.md) of each module.
|
||||
- `RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino)`: Registers module routes to the application's `router`, in order to route [`message`s](./messages-and-queries.md#messages) to the appropriate [`handler`](./handler.md), and module query routes to the application's `queryRouter`, in order to route [`queries`](./messages-and-queries.md#queries) to the appropriate [`querier`](./query-services.md#legacy-queriers).
|
||||
- `RegisterQueryServices(grpcRouter grpc.Server)`: Registers all module gRPC query services.
|
||||
- `RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino)`: Registers legacy [`Msg`](./messages-and-queries.md#messages) and [`querier`](./query-services.md#legacy-queriers) routes.
|
||||
- `RegisterServices(cfg Configurator)`: Registers all module services.
|
||||
- `InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, genesisData map[string]json.RawMessage)`: Calls the [`InitGenesis`](./genesis.md#initgenesis) function of each module when the application is first started, in the order defined in `OrderInitGenesis`. Returns an `abci.ResponseInitChain` to the underlying consensus engine, which can contain validator updates.
|
||||
- `ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler)`: Calls the [`ExportGenesis`](./genesis.md#exportgenesis) function of each module, in the order defined in `OrderExportGenesis`. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required.
|
||||
- `BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock)`: At the beginning of each block, this function is called from [`baseapp`](../core/baseapp.md#beginblock) and, in turn, calls the [`BeginBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderBeginBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseBeginBlock` which contains the aforementioned events.
|
||||
- `EndBlock(ctx sdk.Context, req abci.RequestEndBlock)`: At the end of each block, this function is called from [`baseapp`](../core/baseapp.md#endblock) and, in turn, calls the [`EndBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderEndBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseEndBlock` which contains the aforementioned events, as well as validator set updates (if any).
|
||||
- `BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock)`: At the beginning of each block, this function is called from [`BaseApp`](../core/baseapp.md#beginblock) and, in turn, calls the [`BeginBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderBeginBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseBeginBlock` which contains the aforementioned events.
|
||||
- `EndBlock(ctx sdk.Context, req abci.RequestEndBlock)`: At the end of each block, this function is called from [`BaseApp`](../core/baseapp.md#endblock) and, in turn, calls the [`EndBlock`](./beginblock-endblock.md) function of each module, in the order defined in `OrderEndBlockers`. It creates a child [context](../core/context.md) with an event manager to aggregate [events](../core/events.md) emitted from all modules. The function returns an `abci.ResponseEndBlock` which contains the aforementioned events, as well as validator set updates (if any).
|
||||
|
||||
Here's an example of a concrete integration within an application:
|
||||
|
||||
|
||||
101
docs/building-modules/msg-services.md
Normal file
101
docs/building-modules/msg-services.md
Normal file
@ -0,0 +1,101 @@
|
||||
<!--
|
||||
order: 4
|
||||
-->
|
||||
# `Msg` Services
|
||||
|
||||
A `Msg` Service processes [messages](./messages-and-queries.md#messages). `Msg` Services are specific to the module in which they are defined, and only process messages defined within the said module. They are called from `BaseApp` during [`DeliverTx`](../core/baseapp.md#delivertx). {synopsis}
|
||||
|
||||
## Pre-requisite Readings
|
||||
|
||||
- [Module Manager](./module-manager.md) {prereq}
|
||||
- [Messages and Queries](./messages-and-queries.md) {prereq}
|
||||
|
||||
## Implementation of a module `Msg` service
|
||||
|
||||
All `Msg` processing is done by a [`Msg`](messages-and-queries.md#msg-services) protobuf service. Each module should define a `Msg` service, which will be responsible for request and response serialization.
|
||||
|
||||
As further described in [ADR 031](../architecture/adr-031-msg-service.md), this approach has the advantage of clearly specifying return types and generating server and client code.
|
||||
|
||||
Based on the definition of the `Msg` service, Protobuf generates a `MsgServer` interface. It is the role of the module developer to implement this interface, by implementing the state transition logic that should happen upon receival of each `Msg`. As an example, here is the generated `MsgServer` interface for `x/bank`, which exposes two `Msg`s:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/bank/types/tx.pb.go#L285-L291
|
||||
|
||||
When possible, the existing module's [`Keeper`](keeper.md) should implement `MsgServer`, otherwise a `msgServer` struct that embeds the `Keeper` can be created, typically in `./keeper/msg_server.go`:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/bank/keeper/msg_server.go#L14-L16
|
||||
|
||||
`msgServer` methods can retrieve the `sdk.Context` from the `context.Context` parameter method using the `sdk.UnwrapSDKContext`:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/bank/keeper/msg_server.go#L27
|
||||
|
||||
`Msg` processing usually follows these 2 steps:
|
||||
|
||||
- First, they perform *stateful* checks to make sure the `message` is valid. At this stage, the `message`'s `ValidateBasic()` method has already been called, meaning *stateless* checks on the message (like making sure parameters are correctly formatted) have already been performed. Checks performed in the `msgServer` method can be more expensive and require access to the state. For example, a `msgServer` method for a `transfer` message might check that the sending account has enough funds to actually perform the transfer. To access the state, the `msgServer` method needs to call the [`keeper`'s](./keeper.md) getter functions.
|
||||
- Then, if the checks are successful, the `msgServer` method calls the [`keeper`'s](./keeper.md) setter functions to actually perform the state transition.
|
||||
|
||||
Before returning, `msgServer` methods generally emit one or more [events](../core/events.md) via the `EventManager` held in the `ctx`:
|
||||
|
||||
```go
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
eventType, // e.g. sdk.EventTypeMessage for a message, types.CustomEventType for a custom event defined in the module
|
||||
sdk.NewAttribute(attributeKey, attributeValue),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
These events are relayed back to the underlying consensus engine and can be used by service providers to implement services around the application. Click [here](../core/events.md) to learn more about events.
|
||||
|
||||
The invoked `msgServer` method returns a `proto.Message` response and an `error`. These return values are then wrapped into an `*sdk.Result` or an `error` using `sdk.WrapServiceResult(ctx sdk.Context, res proto.Message, err error)`:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc2/baseapp/msg_service_router.go#L104
|
||||
|
||||
This method takes care of marshaling the `res` parameter to protobuf and attaching any events on the `ctx.EventManager()` to the `sdk.Result`.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/proto/cosmos/base/abci/v1beta1/abci.proto#L81-L95
|
||||
|
||||
## Legacy Amino `Msg`s
|
||||
|
||||
### `handler` type
|
||||
|
||||
The `handler` type defined in the Cosmos SDK will be deprecated in favor of [`Msg` Services](#implementation-of-a-module-msg-service).
|
||||
|
||||
Here is the typical structure of a `handler` function:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc2/types/handler.go#L4
|
||||
|
||||
Let us break it down:
|
||||
|
||||
- The [`Msg`](./messages-and-queries.md#messages) is the actual object being processed.
|
||||
- The [`Context`](../core/context.md) contains all the necessary information needed to process the `msg`, as well as a cache-wrapped copy of the latest state. If the `msg` is succesfully processed, the modified version of the temporary state contained in the `ctx` will be written to the main state.
|
||||
- The [`*Result`] returned to `BaseApp` contains (among other things) information on the execution of the `handler` and [events](../core/events.md).
|
||||
|
||||
Module `handler`s are typically implemented in a `./handler.go` file inside the module's folder. The [module manager](./module-manager.md) is used to add the module's `handler`s to the
|
||||
[application's `router`](../core/baseapp.md#message-routing) via the `Route()` method. Typically,
|
||||
the manager's `Route()` method simply constructs a Route that calls a `NewHandler()` method defined in `handler.go`.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/228728cce2af8d494c8b4e996d011492139b04ab/x/gov/module.go#L143-L146
|
||||
|
||||
### Implementation
|
||||
|
||||
`NewHandler` function dispatches a `Msg` to appropriate handler function, usually by using a switch statement:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/d55c1a26657a0af937fa2273b38dcfa1bb3cff9f/x/bank/handler.go#L13-L29
|
||||
|
||||
First, `NewHandler` function sets a new `EventManager` to the context to isolate events per `msg`.
|
||||
Then, a simple switch calls the appropriate `handler` based on the `Msg` type.
|
||||
|
||||
In this regard, `handler`s functions need to be implemented for each module `Msg`. This will also involve manual handler registration of `Msg` types.
|
||||
`handler`s functions should return a `*Result` and an `error`.
|
||||
|
||||
## Telemetry
|
||||
|
||||
New [telemetry metrics](../core/telemetry.md) can be created from `msgServer` methods when handling messages.
|
||||
|
||||
This is an example from the `x/auth/vesting` module:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc1/x/auth/vesting/msg_server.go#L73-L85
|
||||
|
||||
## Next {hide}
|
||||
|
||||
Learn about [query services](./query-services.md) {hide}
|
||||
@ -4,7 +4,7 @@ order: 5
|
||||
|
||||
# Query Services
|
||||
|
||||
A query service processes [`queries`](./messages-and-queries.md#queries). Query services are specific to the module in which they are defined, and only process `queries` defined within said module. They are called from `baseapp`'s [`Query` method](../core/baseapp.md#query). {synopsis}
|
||||
A query service processes [`queries`](./messages-and-queries.md#queries). Query services are specific to the module in which they are defined, and only process `queries` defined within said module. They are called from `BaseApp`'s [`Query` method](../core/baseapp.md#query). {synopsis}
|
||||
|
||||
## Pre-requisite Readings
|
||||
|
||||
@ -22,7 +22,7 @@ Let us break it down:
|
||||
- The `path` is an array of `string`s that contains the type of the query, and that can also contain `query` arguments. See [`queries`](./messages-and-queries.md#queries) for more information.
|
||||
- The `req` itself is primarily used to retrieve arguments if they are too large to fit in the `path`. This is done using the `Data` field of `req`.
|
||||
- The [`Context`](../core/context.md) contains all the necessary information needed to process the `query`, as well as a cache-wrapped copy of the latest state. It is primarily used by the [`keeper`](./keeper.md) to access the state.
|
||||
- The result `res` returned to `baseapp`, marshalled using the application's [`codec`](../core/encoding.md).
|
||||
- The result `res` returned to `BaseApp`, marshalled using the application's [`codec`](../core/encoding.md).
|
||||
|
||||
## Implementation of a module query service
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ x/{module}
|
||||
│ ├── invariants.go
|
||||
│ ├── genesis.go
|
||||
│ ├── keeper.go
|
||||
│ ├── msg_server.go
|
||||
│ ├── ...
|
||||
│ └── querier.go
|
||||
│ └── grpc_query.go
|
||||
|
||||
@ -8,7 +8,7 @@ parent:
|
||||
|
||||
This repository contains reference documentation on the core concepts of the Cosmos SDK.
|
||||
|
||||
1. [`Baseapp`](./baseapp.md)
|
||||
1. [`BaseApp`](./baseapp.md)
|
||||
2. [Transaction](./transactions.md)
|
||||
3. [Context](./context.md)
|
||||
4. [Node Client](./node.md)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
order: 1
|
||||
-->
|
||||
|
||||
# Baseapp
|
||||
# BaseApp
|
||||
|
||||
This document describes `BaseApp`, the abstraction that implements the core functionalities of an SDK application. {synopsis}
|
||||
|
||||
@ -15,11 +15,9 @@ This document describes `BaseApp`, the abstraction that implements the core func
|
||||
|
||||
`BaseApp` is a base type that implements the core of an SDK application, namely:
|
||||
|
||||
- The [Application Blockchain Interface](#abci), for the state-machine to communicate with the
|
||||
underlying consensus engine (e.g. Tendermint).
|
||||
- A [Router](#routing), to route messages and queries to the appropriate module.
|
||||
- Different [states](#states), as the state-machine can have different volatile
|
||||
states updated based on the ABCI message received.
|
||||
- The [Application Blockchain Interface](#abci), for the state-machine to communicate with the underlying consensus engine (e.g. Tendermint).
|
||||
- [Service Routers](#service-routers), to route messages and queries to the appropriate module.
|
||||
- Different [states](#states), as the state-machine can have different volatile states updated based on the ABCI message received.
|
||||
|
||||
The goal of `BaseApp` is to provide the fundamental layer of an SDK application
|
||||
that developers can easily extend to build their own custom application. Usually,
|
||||
@ -40,67 +38,67 @@ type App struct {
|
||||
|
||||
Extending the application with `BaseApp` gives the former access to all of `BaseApp`'s methods.
|
||||
This allows developers to compose their custom application with the modules they want, while not
|
||||
having to concern themselves with the hard work of implementing the ABCI, the routing and state
|
||||
having to concern themselves with the hard work of implementing the ABCI, the service routers and state
|
||||
management logic.
|
||||
|
||||
## Type Definition
|
||||
|
||||
The `BaseApp` type holds many important parameters for any Cosmos SDK based application.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/baseapp.go#L54-L108
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/baseapp.go#L46-L131
|
||||
|
||||
Let us go through the most important components.
|
||||
|
||||
> __Note__: Not all parameters are described, only the most important ones. Refer to the
|
||||
type definition for the full list.
|
||||
> **Note**: Not all parameters are described, only the most important ones. Refer to the
|
||||
> type definition for the full list.
|
||||
|
||||
First, the important parameters that are initialized during the bootstrapping of the application:
|
||||
|
||||
- [`CommitMultiStore`](./store.md#commitmultistore): This is the main store of the application,
|
||||
which holds the canonical state that is committed at the [end of each block](#commit). This store
|
||||
is **not** cached, meaning it is not used to update the application's volatile (un-committed) states.
|
||||
The `CommitMultiStore` is a multi-store, meaning a store of stores. Each module of the application
|
||||
uses one or multiple `KVStores` in the multi-store to persist their subset of the state.
|
||||
which holds the canonical state that is committed at the [end of each block](#commit). This store
|
||||
is **not** cached, meaning it is not used to update the application's volatile (un-committed) states.
|
||||
The `CommitMultiStore` is a multi-store, meaning a store of stores. Each module of the application
|
||||
uses one or multiple `KVStores` in the multi-store to persist their subset of the state.
|
||||
- Database: The `db` is used by the `CommitMultiStore` to handle data persistence.
|
||||
- [Router](#message-routing): The `router` facilitates the routing of `messages` to the appropriate
|
||||
module for it to be processed. Here a `message` refers to the transaction components that need to be
|
||||
processed by the application in order to update the state, and not to ABCI messages which implement
|
||||
the interface between the application and the underlying consensus engine.
|
||||
- [Query Router](#query-routing): The `query router` facilitates the routing of queries to the
|
||||
appropriate module for it to be processed. These `queries` are not ABCI messages themselves, but they
|
||||
are relayed to the application from the underlying consensus engine via the ABCI message [`Query`](#query).
|
||||
- [`Msg` Service Router](#msg-service-router): The `msgServiceRouter` facilitates the routing of service `Msg`s to the appropriate
|
||||
module for it to be processed. Here a service `Msg` refers to the transaction components that need to be
|
||||
processed by the application in order to update the state, and not to ABCI messages which implement
|
||||
the interface between the application and the underlying consensus engine.
|
||||
- [gRPC Query Router](#grpc-query-router): The `grpcQueryRouter` facilitates the routing of gRPC queries to the
|
||||
appropriate module for it to be processed. These queries are not ABCI messages themselves, but they
|
||||
are relayed to the relevant module's gRPC `Query` service.
|
||||
- [`TxDecoder`](https://godoc.org/github.com/cosmos/cosmos-sdk/types#TxDecoder): It is used to decode
|
||||
raw transaction bytes relayed by the underlying Tendermint engine.
|
||||
raw transaction bytes relayed by the underlying Tendermint engine.
|
||||
- [`ParamStore`](#paramstore): The parameter store used to get and set application consensus parameters.
|
||||
- [`AnteHandler`](#antehandler): This handler is used to handle signature verification, fee payment,
|
||||
and other pre-message execution checks when a transaction is received. It's executed during
|
||||
[`CheckTx/RecheckTx`](#checktx) and [`DeliverTx`](#delivertx).
|
||||
and other pre-message execution checks when a transaction is received. It's executed during
|
||||
[`CheckTx/RecheckTx`](#checktx) and [`DeliverTx`](#delivertx).
|
||||
- [`InitChainer`](../basics/app-anatomy.md#initchainer),
|
||||
[`BeginBlocker` and `EndBlocker`](../basics/app-anatomy.md#beginblocker-and-endblocker): These are
|
||||
the functions executed when the application receives the `InitChain`, `BeginBlock` and `EndBlock`
|
||||
ABCI messages from the underlying Tendermint engine.
|
||||
[`BeginBlocker` and `EndBlocker`](../basics/app-anatomy.md#beginblocker-and-endblocker): These are
|
||||
the functions executed when the application receives the `InitChain`, `BeginBlock` and `EndBlock`
|
||||
ABCI messages from the underlying Tendermint engine.
|
||||
|
||||
Then, parameters used to define [volatile states](#volatile-states) (i.e. cached states):
|
||||
|
||||
- `checkState`: This state is updated during [`CheckTx`](#checktx), and reset on [`Commit`](#commit).
|
||||
- `deliverState`: This state is updated during [`DeliverTx`](#delivertx), and set to `nil` on
|
||||
[`Commit`](#commit) and gets re-initialized on BeginBlock.
|
||||
[`Commit`](#commit) and gets re-initialized on BeginBlock.
|
||||
|
||||
Finally, a few more important parameterd:
|
||||
|
||||
- `voteInfos`: This parameter carries the list of validators whose precommit is missing, either
|
||||
because they did not vote or because the proposer did not include their vote. This information is
|
||||
carried by the [Context](#context) and can be used by the application for various things like
|
||||
punishing absent validators.
|
||||
because they did not vote or because the proposer did not include their vote. This information is
|
||||
carried by the [Context](#context) and can be used by the application for various things like
|
||||
punishing absent validators.
|
||||
- `minGasPrices`: This parameter defines the minimum gas prices accepted by the node. This is a
|
||||
**local** parameter, meaning each full-node can set a different `minGasPrices`. It is used in the
|
||||
`AnteHandler` during [`CheckTx`](#checktx), mainly as a spam protection mechanism. The transaction
|
||||
enters the [mempool](https://tendermint.com/docs/tendermint-core/mempool.html#transaction-ordering)
|
||||
only if the gas prices of the transaction are greater than one of the minimum gas price in
|
||||
`minGasPrices` (e.g. if `minGasPrices == 1uatom,1photon`, the `gas-price` of the transaction must be
|
||||
greater than `1uatom` OR `1photon`).
|
||||
**local** parameter, meaning each full-node can set a different `minGasPrices`. It is used in the
|
||||
`AnteHandler` during [`CheckTx`](#checktx), mainly as a spam protection mechanism. The transaction
|
||||
enters the [mempool](https://tendermint.com/docs/tendermint-core/mempool.html#transaction-ordering)
|
||||
only if the gas prices of the transaction are greater than one of the minimum gas price in
|
||||
`minGasPrices` (e.g. if `minGasPrices == 1uatom,1photon`, the `gas-price` of the transaction must be
|
||||
greater than `1uatom` OR `1photon`).
|
||||
- `appVersion`: Version of the application. It is set in the
|
||||
[application's constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
[application's constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
|
||||
## Constructor
|
||||
|
||||
@ -114,7 +112,7 @@ func NewBaseApp(
|
||||
```
|
||||
|
||||
The `BaseApp` constructor function is pretty straightforward. The only thing worth noting is the
|
||||
possibility to provide additional [`options`](https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/options.go)
|
||||
possibility to provide additional [`options`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/options.go)
|
||||
to the `BaseApp`, which will execute them in order. The `options` are generally `setter` functions
|
||||
for important parameters, like `SetPruning()` to set pruning options or `SetMinGasPrices()` to set
|
||||
the node's `min-gas-prices`.
|
||||
@ -143,7 +141,7 @@ the root `CommitMultiStore`. Any subsequent reads and writes happen on cached ve
|
||||
### CheckTx State Updates
|
||||
|
||||
During `CheckTx`, the `checkState`, which is based off of the last committed state from the root
|
||||
store, is used for any reads and writes. Here we only execute the `AnteHandler` and verify a router
|
||||
store, is used for any reads and writes. Here we only execute the `AnteHandler` and verify a service router
|
||||
exists for every message in the transaction. Note, when we execute the `AnteHandler`, we cache-wrap
|
||||
the already cache-wrapped `checkState`. This has the side effect that if the `AnteHandler` fails,
|
||||
the state transitions won't be reflected in the `checkState` -- i.e. `checkState` is only updated on
|
||||
@ -186,23 +184,23 @@ parameters are non-nil, they are set in the BaseApp's `ParamStore`. Behind the s
|
||||
is actually managed by an `x/params` module `Subspace`. This allows the parameters to be tweaked via
|
||||
on-chain governance.
|
||||
|
||||
## Routing
|
||||
## Service Routers
|
||||
|
||||
When messages and queries are received by the application, they must be routed to the appropriate module in order to be processed. Routing is done via `baseapp`, which holds a `router` for messages, and a `query router` for queries.
|
||||
When messages and queries are received by the application, they must be routed to the appropriate module in order to be processed. Routing is done via `BaseApp`, which holds a `msgServiceRouter` for messages, and a `grpcQueryRouter` for queries.
|
||||
|
||||
### Message Routing
|
||||
### `Msg` Service Router
|
||||
|
||||
[`Message`s](#../building-modules/messages-and-queries.md#messages) need to be routed after they are extracted from transactions, which are sent from the underlying Tendermint engine via the [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) ABCI messages. To do so, `baseapp` holds a `router` which maps `paths` (`string`) to the appropriate module [`handler`](../building-modules/handler.md) using the `.Route(ctx sdk.Context, path string)` function. Usually, the `path` is the name of the module.
|
||||
[`Msg`s](#../building-modules/messages-and-queries.md#messages) need to be routed after they are extracted from transactions, which are sent from the underlying Tendermint engine via the [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx) ABCI messages. To do so, `BaseApp` holds a `msgServiceRouter` which maps fully-qualified service methods (`string`, defined in each module's `Msg` Protobuf service) to the appropriate module's `Msg` server implementation.
|
||||
|
||||
The [default router included in baseapp](https://github.com/cosmos/cosmos-sdk/blob/master/baseapp/router.go) is stateless. However, some applications may want to make use of more stateful routing mechanisms such as allowing governance to disable certain routes or point them to new modules for upgrade purposes. For this reason, the `sdk.Context` is also passed into the `Route` function of the [Router interface](https://github.com/cosmos/cosmos-sdk/blob/master/types/router.go#L12). For a stateless router that doesn't want to make use of this, can just ignore the ctx.
|
||||
The [default `msgServiceRouter` included in `BaseApp`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/msg_service_router.go) is stateless. However, some applications may want to make use of more stateful routing mechanisms such as allowing governance to disable certain routes or point them to new modules for upgrade purposes. For this reason, the `sdk.Context` is also passed into each [route handler inside `msgServiceRouter`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/msg_service_router.go#L31-L32). For a stateless router that doesn't want to make use of this, you can just ignore the `ctx`.
|
||||
|
||||
The application's `router` is initilalized with all the routes using the application's [module manager](../building-modules/module-manager.md#manager), which itself is initialized with all the application's modules in the application's [constructor](../basics/app-anatomy.md#app-constructor).
|
||||
The application's `msgServiceRouter` is initialized with all the routes using the application's [module manager](../building-modules/module-manager.md#manager) (via the `RegisterServices` method), which itself is initialized with all the application's modules in the application's [constructor](../basics/app-anatomy.md#app-constructor).
|
||||
|
||||
### Query Routing
|
||||
### gRPC Query Router
|
||||
|
||||
Similar to `message`s, [`queries`](../building-modules/messages-and-queries.md#queries) need to be routed to the appropriate module's [querier](../building-modules/query-services.md). To do so, `baseapp` holds a `query router`, which maps module names to module `querier`s. The `queryRouter` is called during the initial stages of `query` processing, which is done via the [`Query` ABCI message](#query).
|
||||
Similar to `Msg`s, [`queries`](../building-modules/messages-and-queries.md#queries) need to be routed to the appropriate module's [`Query` service](../building-modules/query-services.md). To do so, `BaseApp` holds a `grpcQueryRouter`, which maps modules' fully-qualified service methods (`string`, defined in their Protobuf `Query` gRPC) to their `Query` server implementation. The `grpcQueryRouter` is called during the initial stages of query processing, which can be either by directly sending a gRPC query to the gRPC endpoint, or via the [`Query` ABCI message](#query) on the Tendermint RPC endpoint.
|
||||
|
||||
Just like the `router`, the `query router` is initilalized with all the query routes using the application's [module manager](../building-modules/module-manager.md), which itself is initialized with all the application's modules in the application's [constructor](../basics/app-anatomy.md#app-constructor).
|
||||
Just like the `msgServiceRouter`, the `grpcQueryRouter` is initialized with all the query routes using the application's [module manager](../building-modules/module-manager.md) (via the `RegisterServices` method), which itself is initialized with all the application's modules in the application's [constructor](../basics/app-anatomy.md#app-constructor).
|
||||
|
||||
## Main ABCI Messages
|
||||
|
||||
@ -211,11 +209,11 @@ The [Application-Blockchain Interface](https://tendermint.com/docs/spec/abci/) (
|
||||
The consensus engine handles two main tasks:
|
||||
|
||||
- The networking logic, which mainly consists in gossiping block parts, transactions and consensus votes.
|
||||
- The consensus logic, which results in the deterministic ordering of transactions in the form of blocks.
|
||||
- The consensus logic, which results in the deterministic ordering of transactions in the form of blocks.
|
||||
|
||||
It is **not** the role of the consensus engine to define the state or the validity of transactions. Generally, transactions are handled by the consensus engine in the form of `[]bytes`, and relayed to the application via the ABCI to be decoded and processed. At keys moments in the networking and consensus processes (e.g. beginning of a block, commit of a block, reception of an unconfirmed transaction, ...), the consensus engine emits ABCI messages for the state-machine to act on.
|
||||
It is **not** the role of the consensus engine to define the state or the validity of transactions. Generally, transactions are handled by the consensus engine in the form of `[]bytes`, and relayed to the application via the ABCI to be decoded and processed. At keys moments in the networking and consensus processes (e.g. beginning of a block, commit of a block, reception of an unconfirmed transaction, ...), the consensus engine emits ABCI messages for the state-machine to act on.
|
||||
|
||||
Developers building on top of the Cosmos SDK need not implement the ABCI themselves, as `baseapp` comes with a built-in implementation of the interface. Let us go through the main ABCI messages that `baseapp` implements: [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx)
|
||||
Developers building on top of the Cosmos SDK need not implement the ABCI themselves, as `BaseApp` comes with a built-in implementation of the interface. Let us go through the main ABCI messages that `BaseApp` implements: [`CheckTx`](#checktx) and [`DeliverTx`](#delivertx)
|
||||
|
||||
### CheckTx
|
||||
|
||||
@ -228,18 +226,18 @@ Unconfirmed transactions are relayed to peers only if they pass `CheckTx`.
|
||||
make them lightweight. In the Cosmos SDK, after [decoding transactions](./encoding.md), `CheckTx()` is implemented
|
||||
to do the following checks:
|
||||
|
||||
1. Extract the `message`s from the transaction.
|
||||
2. Perform _stateless_ checks by calling `ValidateBasic()` on each of the `messages`. This is done
|
||||
1. Extract the `Msg`s from the transaction.
|
||||
2. Perform _stateless_ checks by calling `ValidateBasic()` on each of the `Msg`s. This is done
|
||||
first, as _stateless_ checks are less computationally expensive than _stateful_ checks. If
|
||||
`ValidateBasic()` fail, `CheckTx` returns before running _stateful_ checks, which saves resources.
|
||||
3. Perform non-module related _stateful_ checks on the [account](../basics/accounts.md). This step is mainly about checking
|
||||
that the `message` signatures are valid, that enough fees are provided and that the sending account
|
||||
that the `Msg` signatures are valid, that enough fees are provided and that the sending account
|
||||
has enough funds to pay for said fees. Note that no precise [`gas`](../basics/gas-fees.md) counting occurs here,
|
||||
as `message`s are not processed. Usually, the [`AnteHandler`](../basics/gas-fees.md#antehandler) will check that the `gas` provided
|
||||
as `Msg`s are not processed. Usually, the [`AnteHandler`](../basics/gas-fees.md#antehandler) will check that the `gas` provided
|
||||
with the transaction is superior to a minimum reference gas amount based on the raw transaction size,
|
||||
in order to avoid spam with transactions that provide 0 gas.
|
||||
4. Ensure that a [`Route`](#message-routing) exists for each `message`, but do **not** actually
|
||||
process `message`s. `Message`s only need to be processed when the canonical state need to be updated,
|
||||
4. Ensure that each `Msg`'s fully-qualified service method matches on of the routes inside the `msgServiceRouter`, but do **not** actually
|
||||
process `Msg`s. `Msg`s only need to be processed when the canonical state need to be updated,
|
||||
which happens during `DeliverTx`.
|
||||
|
||||
Steps 2. and 3. are performed by the [`AnteHandler`](../basics/gas-fees.md#antehandler) in the [`RunTx()`](#runtx-antehandler-and-runmsgs)
|
||||
@ -257,11 +255,11 @@ is actually included in a block, because `checkState` never gets committed to th
|
||||
`CheckTx` returns a response to the underlying consensus engine of type [`abci.ResponseCheckTx`](https://tendermint.com/docs/spec/abci/abci.html#messages).
|
||||
The response contains:
|
||||
|
||||
- `Code (uint32)`: Response Code. `0` if successful.
|
||||
- `Code (uint32)`: Response Code. `0` if successful.
|
||||
- `Data ([]byte)`: Result bytes, if any.
|
||||
- `Log (string):` The output of the application's logger. May be non-deterministic.
|
||||
- `Info (string):` Additional information. May be non-deterministic.
|
||||
- `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction.
|
||||
- `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction.
|
||||
- `GasUsed (int64)`: Amount of gas consumed by transaction. During `CheckTx`, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction. Next is an example:
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/ante/basic.go#L104
|
||||
- `Events ([]cmn.KVPair)`: Key-Value tags for filtering and indexing transactions (eg. by account). See [`event`s](./events.md) for more.
|
||||
@ -280,27 +278,27 @@ This allows certain checks like signature verification can be skipped during `Ch
|
||||
|
||||
When the underlying consensus engine receives a block proposal, each transaction in the block needs to be processed by the application. To that end, the underlying consensus engine sends a `DeliverTx` message to the application for each transaction in a sequential order.
|
||||
|
||||
Before the first transaction of a given block is processed, a [volatile state](#volatile-states) called `deliverState` is intialized during [`BeginBlock`](#beginblock). This state is updated each time a transaction is processed via `DeliverTx`, and committed to the [main state](#main-state) when the block is [committed](#commit), after what is is set to `nil`.
|
||||
Before the first transaction of a given block is processed, a [volatile state](#volatile-states) called `deliverState` is intialized during [`BeginBlock`](#beginblock). This state is updated each time a transaction is processed via `DeliverTx`, and committed to the [main state](#main-state) when the block is [committed](#commit), after what is is set to `nil`.
|
||||
|
||||
`DeliverTx` performs the **exact same steps as `CheckTx`**, with a little caveat at step 3 and the addition of a fifth step:
|
||||
|
||||
1. The `AnteHandler` does **not** check that the transaction's `gas-prices` is sufficient. That is because the `min-gas-prices` value `gas-prices` is checked against is local to the node, and therefore what is enough for one full-node might not be for another. This means that the proposer can potentially include transactions for free, although they are not incentivised to do so, as they earn a bonus on the total fee of the block they propose.
|
||||
2. For each `message` in the transaction, route to the appropriate module's [`handler`](../building-modules/handler.md). Additional _stateful_ checks are performed, and the cache-wrapped multistore held in `deliverState`'s `context` is updated by the module's `keeper`. If the `handler` returns successfully, the cache-wrapped multistore held in `context` is written to `deliverState` `CacheMultiStore`.
|
||||
2. For each `Msg` in the transaction, route to the appropriate module's [`Msg` service](../building-modules/msg-services.md). Additional _stateful_ checks are performed, and the cache-wrapped multistore held in `deliverState`'s `context` is updated by the module's `keeper`. If the `Msg` service returns successfully, the cache-wrapped multistore held in `context` is written to `deliverState` `CacheMultiStore`.
|
||||
|
||||
During step 5., each read/write to the store increases the value of `GasConsumed`. You can find the default cost of each operation:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/gas.go#L142-L150
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/gas.go#L153-L162
|
||||
|
||||
At any point, if `GasConsumed > GasWanted`, the function returns with `Code != 0` and `DeliverTx` fails.
|
||||
At any point, if `GasConsumed > GasWanted`, the function returns with `Code != 0` and `DeliverTx` fails.
|
||||
|
||||
`DeliverTx` returns a response to the underlying consensus engine of type [`abci.ResponseDeliverTx`](https://tendermint.com/docs/spec/abci/abci.html#delivertx). The response contains:
|
||||
|
||||
- `Code (uint32)`: Response Code. `0` if successful.
|
||||
- `Code (uint32)`: Response Code. `0` if successful.
|
||||
- `Data ([]byte)`: Result bytes, if any.
|
||||
- `Log (string):` The output of the application's logger. May be non-deterministic.
|
||||
- `Info (string):` Additional information. May be non-deterministic.
|
||||
- `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction.
|
||||
- `GasUsed (int64)`: Amount of gas consumed by transaction. During `DeliverTx`, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction, and by adding gas each time a read/write to the store occurs.
|
||||
- `GasWanted (int64)`: Amount of gas requested for transaction. It is provided by users when they generate the transaction.
|
||||
- `GasUsed (int64)`: Amount of gas consumed by transaction. During `DeliverTx`, this value is computed by multiplying the standard cost of a transaction byte by the size of the raw transaction, and by adding gas each time a read/write to the store occurs.
|
||||
- `Events ([]cmn.KVPair)`: Key-Value tags for filtering and indexing transactions (eg. by account). See [`event`s](./events.md) for more.
|
||||
- `Codespace (string)`: Namespace for the Code.
|
||||
|
||||
@ -308,41 +306,41 @@ At any point, if `GasConsumed > GasWanted`, the function returns with `Code != 0
|
||||
|
||||
### RunTx
|
||||
|
||||
`RunTx` is called from `CheckTx`/`DeliverTx` to handle the transaction, with `runTxModeCheck` or `runTxModeDeliver` as parameter to differentiate between the two modes of execution. Note that when `RunTx` receives a transaction, it has already been decoded.
|
||||
`RunTx` is called from `CheckTx`/`DeliverTx` to handle the transaction, with `runTxModeCheck` or `runTxModeDeliver` as parameter to differentiate between the two modes of execution. Note that when `RunTx` receives a transaction, it has already been decoded.
|
||||
|
||||
The first thing `RunTx` does upon being called is to retrieve the `context`'s `CacheMultiStore` by calling the `getContextForTx()` function with the appropriate mode (either `runTxModeCheck` or `runTxModeDeliver`). This `CacheMultiStore` is a cached version of the main store instantiated during `BeginBlock` for `DeliverTx` and during the `Commit` of the previous block for `CheckTx`. After that, two `defer func()` are called for [`gas`](../basics/gas-fees.md) management. They are executed when `runTx` returns and make sure `gas` is actually consumed, and will throw errors, if any.
|
||||
The first thing `RunTx` does upon being called is to retrieve the `context`'s `CacheMultiStore` by calling the `getContextForTx()` function with the appropriate mode (either `runTxModeCheck` or `runTxModeDeliver`). This `CacheMultiStore` is a cached version of the main store instantiated during `BeginBlock` for `DeliverTx` and during the `Commit` of the previous block for `CheckTx`. After that, two `defer func()` are called for [`gas`](../basics/gas-fees.md) management. They are executed when `runTx` returns and make sure `gas` is actually consumed, and will throw errors, if any.
|
||||
|
||||
After that, `RunTx()` calls `ValidateBasic()` on each `message`in the `Tx`, which runs preliminary _stateless_ validity checks. If any `message` fails to pass `ValidateBasic()`, `RunTx()` returns with an error.
|
||||
After that, `RunTx()` calls `ValidateBasic()` on each `Msg`in the `Tx`, which runs preliminary _stateless_ validity checks. If any `Msg` fails to pass `ValidateBasic()`, `RunTx()` returns with an error.
|
||||
|
||||
Then, the [`anteHandler`](#antehandler) of the application is run (if it exists). In preparation of this step, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are cached-wrapped using the `cacheTxContext()` function.
|
||||
Then, the [`anteHandler`](#antehandler) of the application is run (if it exists). In preparation of this step, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are cached-wrapped using the `cacheTxContext()` function.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/baseapp.go#L587
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/baseapp.go#L623-L630
|
||||
|
||||
This allows `RunTx` not to commit the changes made to the state during the execution of `anteHandler` if it ends up failing. It also prevents the module implementing the `anteHandler` from writing to state, which is an important part of the [object-capabilities](./ocap.md) of the Cosmos SDK.
|
||||
This allows `RunTx` not to commit the changes made to the state during the execution of `anteHandler` if it ends up failing. It also prevents the module implementing the `anteHandler` from writing to state, which is an important part of the [object-capabilities](./ocap.md) of the Cosmos SDK.
|
||||
|
||||
Finally, the [`RunMsgs()`](#runmsgs) function is called to process the `messages`s in the `Tx`. In preparation of this step, just like with the `anteHandler`, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are cached-wrapped using the `cacheTxContext()` function.
|
||||
Finally, the [`RunMsgs()`](#runmsgs) function is called to process the `Msg`s in the `Tx`. In preparation of this step, just like with the `anteHandler`, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are cached-wrapped using the `cacheTxContext()` function.
|
||||
|
||||
### AnteHandler
|
||||
|
||||
The `AnteHandler` is a special handler that implements the `anteHandler` interface and is used to authenticate the transaction before the transaction's internal messages are processed.
|
||||
The `AnteHandler` is a special handler that implements the `AnteHandler` interface and is used to authenticate the transaction before the transaction's internal messages are processed.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/handler.go#L8
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/handler.go#L6-L8
|
||||
|
||||
The `AnteHandler` is theoretically optional, but still a very important component of public blockchain networks. It serves 3 primary purposes:
|
||||
|
||||
- Be a primary line of defense against spam and second line of defense (the first one being the mempool) against transaction replay with fees deduction and [`sequence`](./transactions.md#transaction-generation) checking.
|
||||
- Perform preliminary *stateful* validity checks like ensuring signatures are valid or that the sender has enough funds to pay for fees.
|
||||
- Play a role in the incentivisation of stakeholders via the collection of transaction fees.
|
||||
- Be a primary line of defense against spam and second line of defense (the first one being the mempool) against transaction replay with fees deduction and [`sequence`](./transactions.md#transaction-generation) checking.
|
||||
- Perform preliminary _stateful_ validity checks like ensuring signatures are valid or that the sender has enough funds to pay for fees.
|
||||
- Play a role in the incentivisation of stakeholders via the collection of transaction fees.
|
||||
|
||||
`baseapp` holds an `anteHandler` as paraemter, which is initialized in the [application's constructor](../basics/app-anatomy.md#application-constructor). The most widely used `anteHandler` today is that of the [`auth` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/auth/ante/ante.go).
|
||||
`BaseApp` holds an `anteHandler` as paraemter, which is initialized in the [application's constructor](../basics/app-anatomy.md#application-constructor). The most widely used `anteHandler` today is that of the [`auth` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/auth/ante/ante.go).
|
||||
|
||||
Click [here](../basics/gas-fees.md#antehandler) for more on the `anteHandler`.
|
||||
Click [here](../basics/gas-fees.md#antehandler) for more on the `anteHandler`.
|
||||
|
||||
### RunMsgs
|
||||
|
||||
`RunMsgs` is called from `RunTx` with `runTxModeCheck` as parameter to check the existence of a route for each message the transaction, and with `runTxModeDeliver` to actually process the `message`s.
|
||||
`RunMsgs` is called from `RunTx` with `runTxModeCheck` as parameter to check the existence of a route for each message the transaction, and with `runTxModeDeliver` to actually process the `Msg`s.
|
||||
|
||||
First, it retreives the `message`'s `route` using the `Msg.Route()` method. Then, using the application's [`router`](#routing) and the `route`, it checks for the existence of a `handler`. At this point, if `mode == runTxModeCheck`, `RunMsgs` returns. If instead `mode == runTxModeDeliver`, the [`handler`](../building-modules/handler.md) function for the message is executed, before `RunMsgs` returns.
|
||||
First, it retrieves the `Msg`'s fully-qualified service method name, by checking the `type_url` of the Protobuf `Any` representing the service `Msg`. Then, using the application's [`msgServiceRouter`](#msg-service-router), it checks for the existence of this fully-qualified service method. At this point, if `mode == runTxModeCheck`, `RunMsgs` returns. If instead `mode == runTxModeDeliver`, the [`Msg` server](../building-modules/msg-services.md) implementation for the message is executed, before `RunMsgs` returns.
|
||||
|
||||
## Other ABCI Messages
|
||||
|
||||
@ -350,49 +348,49 @@ First, it retreives the `message`'s `route` using the `Msg.Route()` method. Then
|
||||
|
||||
The [`InitChain` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#initchain) is sent from the underlying Tendermint engine when the chain is first started. It is mainly used to **initialize** parameters and state like:
|
||||
|
||||
- [Consensus Parameters](https://tendermint.com/docs/spec/abci/apps.html#consensus-parameters) via `setConsensusParams`.
|
||||
- [Consensus Parameters](https://tendermint.com/docs/spec/abci/apps.html#consensus-parameters) via `setConsensusParams`.
|
||||
- [`checkState` and `deliverState`](#volatile-states) via `setCheckState` and `setDeliverState`.
|
||||
- The [block gas meter](../basics/gas-fees.md#block-gas-meter), with infinite gas to process genesis transactions.
|
||||
- The [block gas meter](../basics/gas-fees.md#block-gas-meter), with infinite gas to process genesis transactions.
|
||||
|
||||
Finally, the `InitChain(req abci.RequestInitChain)` method of `baseapp` calls the [`initChainer()`](../basics/app-anatomy.md#initchainer) of the application in order to initialize the main state of the application from the `genesis file` and, if defined, call the [`InitGenesis`](../building-modules/genesis.md#initgenesis) function of each of the application's modules.
|
||||
Finally, the `InitChain(req abci.RequestInitChain)` method of `BaseApp` calls the [`initChainer()`](../basics/app-anatomy.md#initchainer) of the application in order to initialize the main state of the application from the `genesis file` and, if defined, call the [`InitGenesis`](../building-modules/genesis.md#initgenesis) function of each of the application's modules.
|
||||
|
||||
### BeginBlock
|
||||
|
||||
The [`BeginBlock` ABCI message](#https://tendermint.com/docs/app-dev/abci-spec.html#beginblock) is sent from the underlying Tendermint engine when a block proposal created by the correct proposer is received, before [`DeliverTx`](#delivertx) is run for each transaction in the block. It allows developers to have logic be executed at the beginning of each block. In the Cosmos SDK, the `BeginBlock(req abci.RequestBeginBlock)` method does the following:
|
||||
|
||||
- Initialize [`deliverState`](#volatile-states) with the latest header using the `req abci.RequestBeginBlock` passed as parameter via the `setDeliverState` function.
|
||||
- Initialize [`deliverState`](#volatile-states) with the latest header using the `req abci.RequestBeginBlock` passed as parameter via the `setDeliverState` function.
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/baseapp/baseapp.go#L387-L397
|
||||
This function also resets the [main gas meter](../basics/gas-fees.md#main-gas-meter).
|
||||
- Initialize the [block gas meter](../basics/gas-fees.md#block-gas-meter) with the `maxGas` limit. The `gas` consumed within the block cannot go above `maxGas`. This parameter is defined in the application's consensus parameters.
|
||||
This function also resets the [main gas meter](../basics/gas-fees.md#main-gas-meter).
|
||||
- Initialize the [block gas meter](../basics/gas-fees.md#block-gas-meter) with the `maxGas` limit. The `gas` consumed within the block cannot go above `maxGas`. This parameter is defined in the application's consensus parameters.
|
||||
- Run the application's [`beginBlocker()`](../basics/app-anatomy.md#beginblocker-and-endblock), which mainly runs the [`BeginBlocker()`](../building-modules/beginblock-endblock.md#beginblock) method of each of the application's modules.
|
||||
- Set the [`VoteInfos`](https://tendermint.com/docs/app-dev/abci-spec.html#voteinfo) of the application, i.e. the list of validators whose *precommit* for the previous block was included by the proposer of the current block. This information is carried into the [`Context`](./context.md) so that it can be used during `DeliverTx` and `EndBlock`.
|
||||
- Set the [`VoteInfos`](https://tendermint.com/docs/app-dev/abci-spec.html#voteinfo) of the application, i.e. the list of validators whose _precommit_ for the previous block was included by the proposer of the current block. This information is carried into the [`Context`](./context.md) so that it can be used during `DeliverTx` and `EndBlock`.
|
||||
|
||||
### EndBlock
|
||||
|
||||
The [`EndBlock` ABCI message](#https://tendermint.com/docs/app-dev/abci-spec.html#endblock) is sent from the underlying Tendermint engine after [`DeliverTx`](#delivertx) as been run for each transaction in the block. It allows developers to have logic be executed at the end of each block. In the Cosmos SDK, the bulk `EndBlock(req abci.RequestEndBlock)` method is to run the application's [`EndBlocker()`](../basics/app-anatomy.md#beginblocker-and-endblock), which mainly runs the [`EndBlocker()`](../building-modules/beginblock-endblock.md#beginblock) method of each of the application's modules.
|
||||
The [`EndBlock` ABCI message](#https://tendermint.com/docs/app-dev/abci-spec.html#endblock) is sent from the underlying Tendermint engine after [`DeliverTx`](#delivertx) as been run for each transaction in the block. It allows developers to have logic be executed at the end of each block. In the Cosmos SDK, the bulk `EndBlock(req abci.RequestEndBlock)` method is to run the application's [`EndBlocker()`](../basics/app-anatomy.md#beginblocker-and-endblock), which mainly runs the [`EndBlocker()`](../building-modules/beginblock-endblock.md#beginblock) method of each of the application's modules.
|
||||
|
||||
### Commit
|
||||
|
||||
The [`Commit` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#commit) is sent from the underlying Tendermint engine after the full-node has received *precommits* from 2/3+ of validators (weighted by voting power). On the `baseapp` end, the `Commit(res abci.ResponseCommit)` function is implemented to commit all the valid state transitions that occured during `BeginBlock`, `DeliverTx` and `EndBlock` and to reset state for the next block.
|
||||
The [`Commit` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#commit) is sent from the underlying Tendermint engine after the full-node has received _precommits_ from 2/3+ of validators (weighted by voting power). On the `BaseApp` end, the `Commit(res abci.ResponseCommit)` function is implemented to commit all the valid state transitions that occured during `BeginBlock`, `DeliverTx` and `EndBlock` and to reset state for the next block.
|
||||
|
||||
To commit state-transitions, the `Commit` function calls the `Write()` function on `deliverState.ms`, where `deliverState.ms` is a cached multistore of the main store `app.cms`. Then, the `Commit` function sets `checkState` to the latest header (obtbained from `deliverState.ctx.BlockHeader`) and `deliverState` to `nil`.
|
||||
|
||||
Finally, `Commit` returns the hash of the commitment of `app.cms` back to the underlying consensus engine. This hash is used as a reference in the header of the next block.
|
||||
Finally, `Commit` returns the hash of the commitment of `app.cms` back to the underlying consensus engine. This hash is used as a reference in the header of the next block.
|
||||
|
||||
### Info
|
||||
|
||||
The [`Info` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#info) is a simple query from the underlying consensus engine, notably used to sync the latter with the application during a handshake that happens on startup. When called, the `Info(res abci.ResponseInfo)` function from `baseapp` will return the application's name, version and the hash of the last commit of `app.cms`.
|
||||
The [`Info` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#info) is a simple query from the underlying consensus engine, notably used to sync the latter with the application during a handshake that happens on startup. When called, the `Info(res abci.ResponseInfo)` function from `BaseApp` will return the application's name, version and the hash of the last commit of `app.cms`.
|
||||
|
||||
### Query
|
||||
|
||||
The [`Query` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#query) is used to serve queries received from the underlying consensus engine, including queries received via RPC like Tendermint RPC. It is the main entrypoint to build interfaces with the application. The application must respect a few rules when implementing the `Query` method, which are outlined [here](https://tendermint.com/docs/app-dev/abci-spec.html#query).
|
||||
The [`Query` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#query) is used to serve queries received from the underlying consensus engine, including queries received via RPC like Tendermint RPC. It used to be the main entrypoint to build interfaces with the application, but with the introduction of [gRPC queries](../building-modules/query-services.md) in Cosmos SDK v0.40, its usage is more limited. The application must respect a few rules when implementing the `Query` method, which are outlined [here](https://tendermint.com/docs/app-dev/abci-spec.html#query).
|
||||
|
||||
Each `query` comes with a `path`, which contains multiple `string`s. By convention, the first element of the `path` (`path[0]`) contains the category of `query` (`app`, `p2p`, `store` or `custom`). The `baseapp` implementation of the `Query(req abci.RequestQuery)` method is a simple dispatcher serving these 4 main categories of queries:
|
||||
Each Tendermint `query` comes with a `path`, which is a `string` which denotes what to query. If the `path` matches a gRPC fully-qualified service method, then `BaseApp` will defer the query to the `grpcQueryRouter` and let it handle it like explained [above](#grpc-query-router). Otherwise, the `path` represents a query that is not (yet) handled by the gRPC router. `BaseApp` splits the `path` string with the `/` delimiter. By convention, the first element of the splitted string (`splitted[0]`) contains the category of `query` (`app`, `p2p`, `store` or `custom` ). The `BaseApp` implementation of the `Query(req abci.RequestQuery)` method is a simple dispatcher serving these 4 main categories of queries:
|
||||
|
||||
- Application-related queries like querying the application's version, which are served via the `handleQueryApp` method.
|
||||
- Direct queries to the multistore, which are served by the `handlerQueryStore` method. These direct queryeis are different from custom queries which go through `app.queryRouter`, and are mainly used by third-party service provider like block explorers.
|
||||
- P2P queries, which are served via the `handleQueryP2P` method. These queries return either `app.addrPeerFilter` or `app.ipPeerFilter` that contain the list of peers filtered by address or IP respectively. These lists are first initialized via `options` in `baseapp`'s [constructor](#constructor).
|
||||
- Custom queries, which encompass most queries, are served via the `handleQueryCustom` method. The `handleQueryCustom` cache-wraps the multistore before using the `queryRoute` obtained from [`app.queryRouter`](#query-routing) to map the query to the appropriate module's `querier`.
|
||||
- P2P queries, which are served via the `handleQueryP2P` method. These queries return either `app.addrPeerFilter` or `app.ipPeerFilter` that contain the list of peers filtered by address or IP respectively. These lists are first initialized via `options` in `BaseApp`'s [constructor](#constructor).
|
||||
- Custom queries, which encompass legacy queries (before the introduction of gRPC queries), are served via the `handleQueryCustom` method. The `handleQueryCustom` cache-wraps the multistore before using the `queryRoute` obtained from `app.queryRouter` to map the query to the appropriate module's [legacy `querier`](../building-modules/query-services.md#legacy-queriers).
|
||||
|
||||
## Next {hide}
|
||||
|
||||
|
||||
@ -15,25 +15,9 @@ The `context` is a data structure intended to be passed from function to functio
|
||||
|
||||
The SDK `Context` is a custom data structure that contains Go's stdlib [`context`](https://golang.org/pkg/context) as its base, and has many additional types within its definition that are specific to the Cosmos SDK. he `Context` is integral to transaction processing in that it allows modules to easily access their respective [store](./store.md#base-layer-kvstores) in the [`multistore`](./store.md#multistore) and retrieve transactional context such as the block header and gas meter.
|
||||
|
||||
```go
|
||||
type Context struct {
|
||||
ctx context.Context
|
||||
ms MultiStore
|
||||
header tmproto.Header
|
||||
chainID string
|
||||
txBytes []byte
|
||||
logger log.Logger
|
||||
voteInfo []abci.VoteInfo
|
||||
gasMeter GasMeter
|
||||
blockGasMeter GasMeter
|
||||
checkTx bool
|
||||
minGasPrice DecCoins
|
||||
consParams *abci.ConsensusParams
|
||||
eventManager *EventManager
|
||||
}
|
||||
```
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/context.go#L16-L39
|
||||
|
||||
- **Context:** The base type is a Go [Context](https://golang.org/pkg/context), which is explained further in the [Go Context Package](#go-context-package) section below.
|
||||
- **Context:** The base type is a Go [Context](https://golang.org/pkg/context), which is explained further in the [Go Context Package](#go-context-package) section below.
|
||||
- **Multistore:** Every application's `BaseApp` contains a [`CommitMultiStore`](./store.md#multistore) which is provided when a `Context` is created. Calling the `KVStore()` and `TransientStore()` methods allows modules to fetch their respective [`KVStore`](./store.md#base-layer-kvstores) using their unique `StoreKey`.
|
||||
- **ABCI Header:** The [header](https://tendermint.com/docs/spec/abci/abci.html#header) is an ABCI type. It carries important information about the state of the blockchain, such as block height and proposer of the current block.
|
||||
- **Chain ID:** The unique identification number of the blockchain a block pertains to.
|
||||
@ -42,10 +26,10 @@ type Context struct {
|
||||
- **VoteInfo:** A list of the ABCI type [`VoteInfo`](https://tendermint.com/docs/spec/abci/abci.html#voteinfo), which includes the name of a validator and a boolean indicating whether they have signed the block.
|
||||
- **Gas Meters:** Specifically, a [`gasMeter`](../basics/gas-fees.md#main-gas-meter) for the transaction currently being processed using the context and a [`blockGasMeter`](../basics/gas-fees.md#block-gas-meter) for the entire block it belongs to. Users specify how much in fees they wish to pay for the execution of their transaction; these gas meters keep track of how much [gas](../basics/gas-fees.md) has been used in the transaction or block so far. If the gas meter runs out, execution halts.
|
||||
- **CheckTx Mode:** A boolean value indicating whether a transaction should be processed in `CheckTx` or `DeliverTx` mode.
|
||||
- **Min Gas Price:** The minimum [gas](../basics/gas-fees.md) price a node is willing to take in order to include a transaction in its block. This price is a local value configured by each node individually, and should therefore **not be used in any functions used in sequences leading to state-transitions**.
|
||||
- **Min Gas Price:** The minimum [gas](../basics/gas-fees.md) price a node is willing to take in order to include a transaction in its block. This price is a local value configured by each node individually, and should therefore **not be used in any functions used in sequences leading to state-transitions**.
|
||||
- **Consensus Params:** The ABCI type [Consensus Parameters](https://tendermint.com/docs/spec/abci/apps.html#consensus-parameters), which specify certain limits for the blockchain, such as maximum gas for a block.
|
||||
- **Event Manager:** The event manager allows any caller with access to a `Context` to emit [`Events`](./events.md). Modules may define module specific
|
||||
`Events` by defining various `Types` and `Attributes` or use the common definitions found in `types/`. Clients can subscribe or query for these `Events`. These `Events` are collected throughout `DeliverTx`, `BeginBlock`, and `EndBlock` and are returned to Tendermint for indexing. For example:
|
||||
`Events` by defining various `Types` and `Attributes` or use the common definitions found in `types/`. Clients can subscribe or query for these `Events`. These `Events` are collected throughout `DeliverTx`, `BeginBlock`, and `EndBlock` and are returned to Tendermint for indexing. For example:
|
||||
|
||||
```go
|
||||
ctx.EventManager().EmitEvent(sdk.NewEvent(
|
||||
@ -63,7 +47,7 @@ are also designed to enable concurrency and to be used in goroutines.
|
||||
Contexts are intended to be **immutable**; they should never be edited. Instead, the convention is
|
||||
to create a child context from its parent using a `With` function. For example:
|
||||
|
||||
``` go
|
||||
```go
|
||||
childCtx = parentCtx.WithBlockHeader(header)
|
||||
```
|
||||
|
||||
@ -79,12 +63,12 @@ goes wrong. The pattern of usage for a Context is as follows:
|
||||
|
||||
1. A process receives a Context `ctx` from its parent process, which provides information needed to
|
||||
perform the process.
|
||||
2. The `ctx.ms` is **cache wrapped**, i.e. a cached copy of the [multistore](./store.md#multistore) is made so that the process can make changes to the state as it executes, without changing the original`ctx.ms`. This is useful to protect the underlying multistore in case the changes need to be reverted at some point in the execution.
|
||||
2. The `ctx.ms` is **cache wrapped**, i.e. a cached copy of the [multistore](./store.md#multistore) is made so that the process can make changes to the state as it executes, without changing the original`ctx.ms`. This is useful to protect the underlying multistore in case the changes need to be reverted at some point in the execution.
|
||||
3. The process may read and write from `ctx` as it is executing. It may call a subprocess and pass
|
||||
`ctx` to it as needed.
|
||||
`ctx` to it as needed.
|
||||
4. When a subprocess returns, it checks if the result is a success or failure. If a failure, nothing
|
||||
needs to be done - the cache wrapped `ctx` is simply discarded. If successful, the changes made to
|
||||
the cache-wrapped `MultiStore` can be committed to the original `ctx.ms` via `Write()`.
|
||||
needs to be done - the cache wrapped `ctx` is simply discarded. If successful, the changes made to
|
||||
the cache-wrapped `MultiStore` can be committed to the original `ctx.ms` via `Write()`.
|
||||
|
||||
For example, here is a snippet from the [`runTx`](./baseapp.md#runtx-and-runmsgs) function in
|
||||
[`baseapp`](./baseapp.md):
|
||||
@ -106,12 +90,12 @@ if result.IsOK() {
|
||||
Here is the process:
|
||||
|
||||
1. Prior to calling `runMsgs` on the message(s) in the transaction, it uses `app.cacheTxContext()`
|
||||
to cache-wrap the context and multistore.
|
||||
to cache-wrap the context and multistore.
|
||||
2. The cache-wrapped context, `runMsgCtx`, is used in `runMsgs` to return a result.
|
||||
3. If the process is running in [`checkTxMode`](./baseapp.md#checktx), there is no need to write the
|
||||
changes - the result is returned immediately.
|
||||
changes - the result is returned immediately.
|
||||
4. If the process is running in [`deliverTxMode`](./baseapp.md#delivertx) and the result indicates
|
||||
a successful run over all the messages, the cached multistore is written back to the original.
|
||||
a successful run over all the messages, the cached multistore is written back to the original.
|
||||
|
||||
## Next {hide}
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ Events are returned to the underlying consensus engine in the response of the fo
|
||||
- [`DeliverTx`](./baseapp.md#delivertx)
|
||||
|
||||
Events, the `type` and `attributes`, are defined on a **per-module basis** in the module's
|
||||
`/types/events.go` file, and triggered from the module's [`handler`](../building-modules/handler.md)
|
||||
`/types/events.go` file, and triggered from the module's [`Msg` service](../building-modules/msg-services.md)
|
||||
via the [`EventManager`](#eventmanager). In addition, each module documents its events under
|
||||
`spec/xx_events.md`.
|
||||
|
||||
@ -68,7 +68,7 @@ func NewHandler(keeper Keeper) sdk.Handler {
|
||||
switch msg := msg.(type) {
|
||||
```
|
||||
|
||||
See the [`Handler`](../building-modules/handler.md) concept doc for a more detailed
|
||||
See the [`Msg` services](../building-modules/msg-services.md) concept doc for a more detailed
|
||||
view on how to typically implement `Events` and use the `EventManager` in modules.
|
||||
|
||||
## Subscribing to Events
|
||||
|
||||
@ -12,60 +12,65 @@ The main endpoint of an SDK application is the daemon client, otherwise known as
|
||||
|
||||
## `main` function
|
||||
|
||||
The full-node client of any SDK application is built by running a `main` function. The client is generally named by appending the `-d` suffix to the application name (e.g. `appd` for an application named `app`), and the `main` function is defined in a `./cmd/appd/main.go` file. Running this function creates an executable `.appd` that comes with a set of commands. For an app named `app`, the main command is [`appd start`](#start-command), which starts the full-node.
|
||||
The full-node client of any SDK application is built by running a `main` function. The client is generally named by appending the `-d` suffix to the application name (e.g. `appd` for an application named `app`), and the `main` function is defined in a `./appd/cmd/main.go` file. Running this function creates an executable `appd` that comes with a set of commands. For an app named `app`, the main command is [`appd start`](#start-command), which starts the full-node.
|
||||
|
||||
In general, developers will implement the `main.go` function with the following structure:
|
||||
|
||||
- First, a [`codec`](./encoding.md) is instanciated for the application.
|
||||
- First, an [`appCodec`](./encoding.md) is instanciated for the application.
|
||||
- Then, the `config` is retrieved and config parameters are set. This mainly involves setting the bech32 prefixes for [addresses and pubkeys](../basics/accounts.md#addresses-and-pubkeys).
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/config.go#L10-L21
|
||||
- Using [cobra](https://github.com/spf13/cobra), the root command of the full-node client is created. After that, all the custom commands of the application are added using the `AddCommand()` method of `rootCmd`.
|
||||
- Add default server commands to `rootCmd` using the `server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators)` method. These commands are separated from the ones added above since they are standard and defined at SDK level. They should be shared by all SDK-based applications. They include the most important command: the [`start` command](#start-command).
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/config.go#L13-L24
|
||||
- Using [cobra](https://github.com/spf13/cobra), the root command of the full-node client is created. After that, all the custom commands of the application are added using the `AddCommand()` method of `rootCmd`.
|
||||
- Add default server commands to `rootCmd` using the `server.AddCommands()` method. These commands are separated from the ones added above since they are standard and defined at SDK level. They should be shared by all SDK-based applications. They include the most important command: the [`start` command](#start-command).
|
||||
- Prepare and execute the `executor`.
|
||||
+++ https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/libs/cli/setup.go#L75-L78
|
||||
+++ https://github.com/tendermint/tendermint/blob/v0.34.0-rc6/libs/cli/setup.go#L74-L78
|
||||
|
||||
See an example of `main` function from the [`gaia`](https://github.com/cosmos/gaia) application:
|
||||
See an example of `main` function from the `simapp` application, the SDK's application for demo purposes:
|
||||
|
||||
+++ https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/cmd/gaiad/main.go
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/simd/main.go
|
||||
|
||||
## `start` command
|
||||
|
||||
The `start` command is defined in the `/server` folder of the Cosmos SDK. It is added to the root command of the full-node client in the [`main` function](#main-function) and called by the end-user to start their node:
|
||||
|
||||
```go
|
||||
// For an example app named "app", the following command starts the full-node
|
||||
|
||||
```bash
|
||||
# For an example app named "app", the following command starts the full-node.
|
||||
appd start
|
||||
|
||||
# Using the SDK's own simapp, the following commands start the simapp node.
|
||||
simd start
|
||||
```
|
||||
|
||||
As a reminder, the full-node is composed of three conceptual layers: the networking layer, the consensus layer and the application layer. The first two are generally bundled together in an entity called the consensus engine (Tendermint Core by default), while the third is the state-machine defined with the help of the Cosmos SDK. Currently, the Cosmos SDK uses Tendermint as the default consensus engine, meaning the start command is implemented to boot up a Tendermint node.
|
||||
As a reminder, the full-node is composed of three conceptual layers: the networking layer, the consensus layer and the application layer. The first two are generally bundled together in an entity called the consensus engine (Tendermint Core by default), while the third is the state-machine defined with the help of the Cosmos SDK. Currently, the Cosmos SDK uses Tendermint as the default consensus engine, meaning the start command is implemented to boot up a Tendermint node.
|
||||
|
||||
The flow of the `start` command is pretty straightforward. First, it retrieves the `config` from the `context` in order to open the `db` (a [`leveldb`](https://github.com/syndtr/goleveldb) instance by default). This `db` contains the latest known state of the application (empty if the application is started from the first time.
|
||||
The flow of the `start` command is pretty straightforward. First, it retrieves the `config` from the `context` in order to open the `db` (a [`leveldb`](https://github.com/syndtr/goleveldb) instance by default). This `db` contains the latest known state of the application (empty if the application is started from the first time.
|
||||
|
||||
With the `db`, the `start` command creates a new instance of the application using an `appCreator` function:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/server/start.go#L144
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/start.go#L227
|
||||
|
||||
Note that an `appCreator` is a function that fulfills the `AppCreator` signature. In practice, the [constructor the application](../basics/app-anatomy.md#constructor-function) is passed as the `appCreator`.
|
||||
Note that an `appCreator` is a function that fulfills the `AppCreator` signature:
|
||||
+++https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/types/app.go#L48-L50
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/server/constructors.go#L17-L25
|
||||
In practice, the [constructor of the application](../basics/app-anatomy.md#constructor-function) is passed as the `appCreator`.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/simapp/simd/cmd/root.go#L170-L215
|
||||
|
||||
Then, the instance of `app` is used to instanciate a new Tendermint node:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/server/start.go#L153-L163
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/start.go#L235-L244
|
||||
|
||||
The Tendermint node can be created with `app` because the latter satisfies the [`abci.Application` interface](https://github.com/tendermint/tendermint/blob/bc572217c07b90ad9cee851f193aaa8e9557cbc7/abci/types/application.go#L11-L26) (given that `app` extends [`baseapp`](./baseapp.md)). As part of the `NewNode` method, Tendermint makes sure that the height of the application (i.e. number of blocks since genesis) is equal to the height of the Tendermint node. The difference between these two heights should always be negative or null. If it is strictly negative, `NewNode` will replay blocks until the height of the application reaches the height of the Tendermint node. Finally, if the height of the application is `0`, the Tendermint node will call [`InitChain`](./baseapp.md#initchain) on the application to initialize the state from the genesis file.
|
||||
The Tendermint node can be created with `app` because the latter satisfies the [`abci.Application` interface](https://github.com/tendermint/tendermint/blob/v0.34.0/abci/types/application.go#L7-L32) (given that `app` extends [`baseapp`](./baseapp.md)). As part of the `NewNode` method, Tendermint makes sure that the height of the application (i.e. number of blocks since genesis) is equal to the height of the Tendermint node. The difference between these two heights should always be negative or null. If it is strictly negative, `NewNode` will replay blocks until the height of the application reaches the height of the Tendermint node. Finally, if the height of the application is `0`, the Tendermint node will call [`InitChain`](./baseapp.md#initchain) on the application to initialize the state from the genesis file.
|
||||
|
||||
Once the Tendermint node is instanciated and in sync with the application, the node can be started:
|
||||
|
||||
```go
|
||||
if err := tmNode.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
```
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/server/start.go#L250-L252
|
||||
|
||||
Upon starting, the node will bootstrap its RPC and P2P server and start dialing peers. During handshake with its peers, if the node realizes they are ahead, it will query all the blocks sequentially in order to catch up. Then, it will wait for new block proposals and block signatures from validators in order to make progress.
|
||||
Upon starting, the node will bootstrap its RPC and P2P server and start dialing peers. During handshake with its peers, if the node realizes they are ahead, it will query all the blocks sequentially in order to catch up. Then, it will wait for new block proposals and block signatures from validators in order to make progress.
|
||||
|
||||
## Other commands
|
||||
|
||||
To discover how to concretely run a node and interact with it, please refer to our [Running a Node](../run-node/README.md) guide.
|
||||
|
||||
## Next {hide}
|
||||
|
||||
Learn about the [store](./store.md) {hide}
|
||||
Learn about the [store](./store.md) {hide}
|
||||
|
||||
@ -58,11 +58,11 @@ The Cosmos SDK comes with a large set of stores to persist the state of applicat
|
||||
|
||||
At its very core, a Cosmos SDK `store` is an object that holds a `CacheWrapper` and implements a `GetStoreType()` method:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L12-L15
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/store.go#L15-L18
|
||||
|
||||
The `GetStoreType` is a simple method that returns the type of store, whereas a `CacheWrapper` is a simple interface that specifies cache-wrapping and `Write` methods:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L217-L238
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/store.go#L240-L264
|
||||
|
||||
Cache-wrapping is used ubiquitously in the Cosmos SDK and required to be implemented on every store type. A cache-wrapper creates a light snapshot of a store that can be passed around and updated without affecting the main underlying store. This is used to trigger temporary state-transitions that may be reverted later should an error occur. If a state-transition sequence is performed without issue, the cached store can be committed to the underlying store at the end of the sequence.
|
||||
|
||||
@ -70,11 +70,11 @@ Cache-wrapping is used ubiquitously in the Cosmos SDK and required to be impleme
|
||||
|
||||
A commit store is a store that has the ability to commit changes made to the underlying tree or db. The Cosmos SDK differentiates simple stores from commit stores by extending the basic store interfaces with a `Committer`:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L24-L28
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/store.go#L29-L33
|
||||
|
||||
The `Committer` is an interface that defines methods to persist changes to disk:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L17-L22
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/store.go#L20-L27
|
||||
|
||||
The `CommitID` is a deterministic commit of the state tree. Its hash is returned to the underlying consensus engine and stored in the block header. Note that commit store interfaces exist for various purposes, one of which is to make sure not every object can commit the store. As part of the [object-capabilities model](./ocap.md) of the Cosmos SDK, only `baseapp` should have the ability to commit stores. For example, this is the reason why the `ctx.KVStore()` method by which modules typically access stores returns a `KVStore` and not a `CommitKVStore`.
|
||||
|
||||
@ -86,27 +86,27 @@ The Cosmos SDK comes with many types of stores, the most used being [`CommitMult
|
||||
|
||||
Each Cosmos SDK application holds a multistore at its root to persist its state. The multistore is a store of `KVStores` that follows the `Multistore` interface:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L83-L112
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/store.go#L104-L133
|
||||
|
||||
If tracing is enabled, then cache-wrapping the multistore will wrap all the underlying `KVStore` in [`TraceKv.Store`](#tracekv-store) before caching them.
|
||||
If tracing is enabled, then cache-wrapping the multistore will wrap all the underlying `KVStore` in [`TraceKv.Store`](#tracekv-store) before caching them.
|
||||
|
||||
### CommitMultiStore
|
||||
|
||||
The main type of `Multistore` used in the Cosmos SDK is `CommitMultiStore`, which is an extension of the `Multistore` interface:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L120-L158
|
||||
+++https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/store.go#L141-L184
|
||||
|
||||
As for concrete implementation, the [`rootMulti.Store`] is the go-to implementation of the `CommitMultiStore` interface.
|
||||
As for concrete implementation, the [`rootMulti.Store`] is the go-to implementation of the `CommitMultiStore` interface.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/rootmulti/store.go#L27-L43
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/rootmulti/store.go#L43-L61
|
||||
|
||||
The `rootMulti.Store` is a base-layer multistore built around a `db` on top of which multiple `KVStores` can be mounted, and is the default multistore store used in [`baseapp`](./baseapp.md).
|
||||
|
||||
### CacheMultiStore
|
||||
|
||||
Whenever the `rootMulti.Store` needs to be cached-wrapped, a [`cachemulti.Store`](https://github.com/cosmos/cosmos-sdk/blob/master/store/cachemulti/store.go) is used.
|
||||
Whenever the `rootMulti.Store` needs to be cached-wrapped, a [`cachemulti.Store`](https://github.com/cosmos/cosmos-sdk/blob/master/store/cachemulti/store.go) is used.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/cachemulti/store.go#L17-L28
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/cachemulti/store.go#L17-L28
|
||||
|
||||
`cachemulti.Store` cache wraps all substores in its constructor and hold them in `Store.stores`. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on all the substores.
|
||||
|
||||
@ -114,50 +114,37 @@ Whenever the `rootMulti.Store` needs to be cached-wrapped, a [`cachemulti.Store`
|
||||
|
||||
### `KVStore` and `CommitKVStore` Interfaces
|
||||
|
||||
A `KVStore` is a simple key-value store used to store and retrieve data. A `CommitKVStore` is a `KVStore` that also implements a `Committer`. By default, stores mounted in `baseapp`'s main `CommitMultiStore` are `CommitKVStore`s. The `KVStore` interface is primarily used to restrict modules from accessing the committer.
|
||||
A `KVStore` is a simple key-value store used to store and retrieve data. A `CommitKVStore` is a `KVStore` that also implements a `Committer`. By default, stores mounted in `baseapp`'s main `CommitMultiStore` are `CommitKVStore`s. The `KVStore` interface is primarily used to restrict modules from accessing the committer.
|
||||
|
||||
Individual `KVStore`s are used by modules to manage a subset of the global state. `KVStores` can be accessed by objects that hold a specific key. This `key` should only be exposed to the [`keeper`](../building-modules/keeper.md) of the module that defines the store.
|
||||
|
||||
`CommitKVStore`s are declared by proxy of their respective `key` and mounted on the application's [multistore](#multistore) in the [main application file](../basics/app-anatomy.md#core-application-file). In the same file, the `key` is also passed to the module's `keeper` that is responsible for managing the store.
|
||||
`CommitKVStore`s are declared by proxy of their respective `key` and mounted on the application's [multistore](#multistore) in the [main application file](../basics/app-anatomy.md#core-application-file). In the same file, the `key` is also passed to the module's `keeper` that is responsible for managing the store.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/store.go#L163-L193
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/store.go#L189-L219
|
||||
|
||||
Apart from the traditional `Get` and `Set` methods, a `KVStore` is expected to implement an `Iterator()` method which returns an `Iterator` object. The `Iterator()` method is used to iterate over a domain of keys, typically keys that share a common prefix. Here is a common pattern of using an `Iterator` that might be found in a module's `keeper`:
|
||||
Apart from the traditional `Get` and `Set` methods, a `KVStore` must provide an `Iterator(start, end)` method which returns an `Iterator` object. It is used to iterate over a range of keys, typically keys that share a common prefix. Below is an example from the bank's module keeper, used to iterate over all account balances:
|
||||
|
||||
```go
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, prefix) // proxy for store.Iterator
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var object types.Object
|
||||
keeper.cdc.MustUnmarshalBinaryBare(iterator.Value(), &object)
|
||||
|
||||
if cb(object) {
|
||||
break
|
||||
}
|
||||
}
|
||||
```
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/bank/keeper/view.go#L115-L134
|
||||
|
||||
### `IAVL` Store
|
||||
|
||||
The default implementation of `KVStore` and `CommitKVStore` used in `baseapp` is the `iavl.Store`.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/iavl/store.go#L32-L47
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/iavl/store.go#L37-L40
|
||||
|
||||
`iavl` stores are based around an [IAVL Tree](https://github.com/tendermint/iavl), a self-balancing binary tree which guarantees that:
|
||||
`iavl` stores are based around an [IAVL Tree](https://github.com/tendermint/iavl), a self-balancing binary tree which guarantees that:
|
||||
|
||||
- `Get` and `Set` operations are O(log n), where n is the number of elements in the tree.
|
||||
- Iteration efficiently returns the sorted elements within the range.
|
||||
- Each tree version is immutable and can be retrieved even after a commit (depending on the pruning settings).
|
||||
- Each tree version is immutable and can be retrieved even after a commit (depending on the pruning settings).
|
||||
|
||||
The documentation on the IAVL Tree is located [here](https://github.com/tendermint/iavl/blob/f9d4b446a226948ed19286354f0d433a887cc4a3/docs/overview.md).
|
||||
The documentation on the IAVL Tree is located [here](https://github.com/cosmos/iavl/blob/v0.15.0-rc5/docs/overview.md).
|
||||
|
||||
### `DbAdapter` Store
|
||||
|
||||
`dbadapter.Store` is a adapter for `dbm.DB` making it fulfilling the `KVStore` interface.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/dbadapter/store.go#L13-L16
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/dbadapter/store.go#L13-L16
|
||||
|
||||
`dbadapter.Store` embeds `dbm.DB`, meaning most of the `KVStore` interface functions are implemented. The other functions (mostly miscellaneous) are manually implemented. This store is primarily used within [Transient Stores](#transient-stores)
|
||||
|
||||
@ -165,17 +152,17 @@ The documentation on the IAVL Tree is located [here](https://github.com/tendermi
|
||||
|
||||
`Transient.Store` is a base-layer `KVStore` which is automatically discarded at the end of the block.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/transient/store.go#L14-L17
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/transient/store.go#L13-L16
|
||||
|
||||
`Transient.Store` is a `dbadapter.Store` with a `dbm.NewMemDB()`. All `KVStore` methods are reused. When `Store.Commit()` is called, a new `dbadapter.Store` is assigned, discarding previous reference and making it garbage collected.
|
||||
|
||||
This type of store is useful to persist information that is only relevant per-block. One example would be to store parameter changes (i.e. a bool set to `true` if a parameter changed in a block).
|
||||
This type of store is useful to persist information that is only relevant per-block. One example would be to store parameter changes (i.e. a bool set to `true` if a parameter changed in a block).
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/params/subspace/subspace.go#L24-L32
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/params/types/subspace.go#L20-L30
|
||||
|
||||
Transient stores are typically accessed via the [`context`](./context.md) via the `TransientStore()` method:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/context.go#L215-L218
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/context.go#L232-L235
|
||||
|
||||
## KVStore Wrappers
|
||||
|
||||
@ -183,9 +170,9 @@ Transient stores are typically accessed via the [`context`](./context.md) via th
|
||||
|
||||
`cachekv.Store` is a wrapper `KVStore` which provides buffered writing / cached reading functionalities over the underlying `KVStore`.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/cachekv/store.go#L26-L33
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/cachekv/store.go#L27-L34
|
||||
|
||||
This is the type used whenever an IAVL Store needs to be cache-wrapped (typically when setting value that might be reverted later).
|
||||
This is the type used whenever an IAVL Store needs to be cache-wrapped (typically when setting value that might be reverted later).
|
||||
|
||||
#### `Get`
|
||||
|
||||
@ -201,27 +188,27 @@ This is the type used whenever an IAVL Store needs to be cache-wrapped (typicall
|
||||
|
||||
### `GasKv` Store
|
||||
|
||||
Cosmos SDK applications use [`gas`](../basics/gas-fees.md) to track resources usage and prevent spam. [`GasKv.Store`](https://github.com/cosmos/cosmos-sdk/blob/master/store/gaskv/store.go) is a `KVStore` wrapper that enables automatic gas consumption each time a read or write to the store is made. It is the solution of choice to track storage usage in Cosmos SDK applications.
|
||||
Cosmos SDK applications use [`gas`](../basics/gas-fees.md) to track resources usage and prevent spam. [`GasKv.Store`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/gaskv/store.go) is a `KVStore` wrapper that enables automatic gas consumption each time a read or write to the store is made. It is the solution of choice to track storage usage in Cosmos SDK applications.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/gaskv/store.go#L11-L17
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/gaskv/store.go#L13-L19
|
||||
|
||||
When methods of the parent `KVStore` are called, `GasKv.Store` automatically consumes appropriate amount of gas depending on the `Store.gasConfig`:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/gas.go#L141-L150
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/gas.go#L153-L162
|
||||
|
||||
By default, all `KVStores` are wrapped in `GasKv.Stores` when retrieved. This is done in the `KVStore()` method of the [`context`](./context.md):
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/context.go#L210-L213
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/context.go#L227-L230
|
||||
|
||||
In this case, the default gas configuration is used:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/types/gas.go#L152-L163
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/gas.go#L164-L175
|
||||
|
||||
### `TraceKv` Store
|
||||
|
||||
`tracekv.Store` is a wrapper `KVStore` which provides operation tracing functionalities over the underlying `KVStore`. It is applied automatically by the Cosmos SDK on all `KVStore` if tracing is enabled on the parent `MultiStore`.
|
||||
`tracekv.Store` is a wrapper `KVStore` which provides operation tracing functionalities over the underlying `KVStore`. It is applied automatically by the Cosmos SDK on all `KVStore` if tracing is enabled on the parent `MultiStore`.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/tracekv/store.go#L20-L43
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/tracekv/store.go#L20-L43
|
||||
|
||||
When each `KVStore` methods are called, `tracekv.Store` automatically logs `traceOperation` to the `Store.writer`. `traceOperation.Metadata` is filled with `Store.context` when it is not nil. `TraceContext` is a `map[string]interface{}`.
|
||||
|
||||
@ -229,7 +216,7 @@ When each `KVStore` methods are called, `tracekv.Store` automatically logs `trac
|
||||
|
||||
`prefix.Store` is a wrapper `KVStore` which provides automatic key-prefixing functionalities over the underlying `KVStore`.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/store/prefix/store.go#L17-L20
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/prefix/store.go#L15-L21
|
||||
|
||||
When `Store.{Get, Set}()` is called, the store forwards the call to its parent, with the key prefixed with the `Store.prefix`.
|
||||
|
||||
|
||||
@ -8,84 +8,146 @@ order: 2
|
||||
|
||||
## Pre-requisite Readings
|
||||
|
||||
* [Anatomy of an SDK Application](../basics/app-anatomy.md) {prereq}
|
||||
- [Anatomy of an SDK Application](../basics/app-anatomy.md) {prereq}
|
||||
|
||||
## Transactions
|
||||
|
||||
Transactions are comprised of metadata held in [contexts](./context.md) and [messages](../building-modules/messages-and-queries.md) that trigger state changes within a module through the module's [Handler](../building-modules/handler.md).
|
||||
Transactions are comprised of metadata held in [contexts](./context.md) and [`Msg`s](../building-modules/messages-and-queries.md) that trigger state changes within a module through the module's [`Msg` service](../building-modules/msg-services.md).
|
||||
|
||||
When users want to interact with an application and make state changes (e.g. sending coins), they create transactions. Each of a transaction's `message`s must be signed using the private key associated with the appropriate account(s), before the transaction is broadcasted to the network. A transaction must then be included in a block, validated, and approved by the network through the consensus process. To read more about the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md).
|
||||
When users want to interact with an application and make state changes (e.g. sending coins), they create transactions. Each of a transaction's `Msg`s must be signed using the private key associated with the appropriate account(s), before the transaction is broadcasted to the network. A transaction must then be included in a block, validated, and approved by the network through the consensus process. To read more about the lifecycle of a transaction, click [here](../basics/tx-lifecycle.md).
|
||||
|
||||
## Type Definition
|
||||
|
||||
Transaction objects are SDK types that implement the `Tx` interface
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/tx_msg.go#L34-L41
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/types/tx_msg.go#L49-L57
|
||||
|
||||
It contains the following methods:
|
||||
|
||||
* **GetMsgs:** unwraps the transaction and returns a list of its message(s) - one transaction may have one or multiple [messages](../building-modules/messages-and-queries.md#messages), which are defined by module developers.
|
||||
* **ValidateBasic:** includes lightweight, [*stateless*](../basics/tx-lifecycle.md#types-of-checks) checks used by ABCI messages [`CheckTx`](./baseapp.md#checktx) and [`DeliverTx`](./baseapp.md#delivertx) to make sure transactions are not invalid. For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth) module's `StdTx` `ValidateBasic` function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user's maximum. Note that this function is to be distinct from the `ValidateBasic` functions for *`messages`*, which perform basic validity checks on messages only. For example, when [`runTx`](./baseapp.md#runtx) is checking a transaction created from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module, it first runs `ValidateBasic` on each message, then runs the `auth` module AnteHandler which calls `ValidateBasic` for the transaction itself.
|
||||
* **TxEncoder:** Nodes running the consensus engine (e.g. Tendermint Core) are responsible for gossiping transactions and ordering them into blocks, but only handle them in the generic `[]byte` form. Transactions are always [marshaled](./encoding.md) (encoded) before they are relayed to nodes, which compacts them to facilitate gossiping and helps maintain the consensus engine's separation from from application logic. The Cosmos SDK allows developers to specify any deterministic encoding format for their applications; the default is Amino.
|
||||
* **TxDecoder:** [ABCI](https://tendermint.com/docs/spec/abci/) calls from the consensus engine to the application, such as `CheckTx` and `DeliverTx`, are used to process transaction data to determine validity and state changes. Since transactions are passed in as `txBytes []byte`, they need to first be unmarshaled (decoded) using `TxDecoder` before any logic is applied.
|
||||
- **GetMsgs:** unwraps the transaction and returns a list of its `Msg`s - one transaction may have one or multiple [`Msg`s](../building-modules/messages-and-queries.md#messages), which are defined by module developers.
|
||||
- **ValidateBasic:** includes lightweight, [_stateless_](../basics/tx-lifecycle.md#types-of-checks) checks used by ABCI messages [`CheckTx`](./baseapp.md#checktx) and [`DeliverTx`](./baseapp.md#delivertx) to make sure transactions are not invalid. For example, the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth) module's `StdTx` `ValidateBasic` function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user's maximum. Note that this function is to be distinct from the `ValidateBasic` functions for `Msg`s, which perform basic validity checks on messages only. For example, when [`runTx`](./baseapp.md#runtx) is checking a transaction created from the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module, it first runs `ValidateBasic` on each message, then runs the `auth` module AnteHandler which calls `ValidateBasic` for the transaction itself.
|
||||
|
||||
The most used implementation of the `Tx` interface is [`StdTx` from the `auth` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/auth/types/stdtx.go). As a developer, using `StdTx` as your transaction format is as simple as importing the `auth` module in your application (which can be done in the [constructor of the application](../basics/app-anatomy.md#constructor-function))
|
||||
As a developer, you should rarely manipulate `Tx` directly, as `Tx` is really an intermediate type used for transaction generation. Instead, developers should prefer the `TxBuilder` interface, which you can learn more about [below](#transaction-generation).
|
||||
|
||||
### Signing Transactions
|
||||
|
||||
Every message in a transaction must be signed by the addresses specified by its `GetSigners`. The SDK currently allows signing transactions in two different ways.
|
||||
|
||||
#### `SIGN_MODE_DIRECT` (preferred)
|
||||
|
||||
The most used implementation of the `Tx` interface is the Protobuf `Tx` message, which is used in `SIGN_MODE_DIRECT`:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/tx.proto#L12-L25
|
||||
|
||||
Because Protobuf serialization is not deterministic, the SDK uses an additional `TxRaw` type to denote the pinned bytes over which a transaction is signed. Any user can generate a valid `body` and `auth_info` for a transaction, and serialize these two messages using Protobuf. `TxRaw` then pins the user's exact binary representation of `body` and `auth_info`, called respectively `body_bytes` and `auth_info_bytes`. The document that is signed by all signers of the transaction is `SignDoc` (deterministically serialized using [ADR-027](../architecture/adr-027-deterministic-protobuf-serialization.md)):
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/tx.proto#L47-L64
|
||||
|
||||
Once signed by all signers, the `body_bytes`, `auth_info_bytes` and `signatures` are gathered into `TxRaw`, whose serialized bytes are broadcasted over the network.
|
||||
|
||||
#### `SIGN_MODE_LEGACY_AMINO_JSON`
|
||||
|
||||
The legacy implemention of the `Tx` interface is the `StdTx` struct from `x/auth`:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/legacy/legacytx/stdtx.go#L120-L130
|
||||
|
||||
The document signed by all signers is `StdSignDoc`:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/legacy/legacytx/stdsign.go#L20-L33
|
||||
|
||||
which is encoded into bytes using Amino JSON. Once all signatures are gathered into `StdTx`, `StdTx` is serialized using Amino JSON, and these bytes are broadcasted over the network.
|
||||
|
||||
#### Other Sign Modes
|
||||
|
||||
Other sign modes, most notably `SIGN_MODE_TEXTUAL`, are being discussed. If you wish to learn more about them, please refer to [ADR-020](../architecture/adr-020-protobuf-transaction-encoding.md).
|
||||
|
||||
## Transaction Process
|
||||
|
||||
A transaction is created by an end-user through one of the possible [interfaces](#interfaces). In the process, two contexts and an array of [messages](#messages) are created, which are then used to [generate](#transaction-generation) the transaction itself. The actual state changes triggered by transactions are enabled by the [handlers](#handlers). The rest of the document will describe each of these components, in this order.
|
||||
The process of an end-user sending a transaction is:
|
||||
|
||||
### CLI and REST Interfaces
|
||||
- decide on the messages to put into the transaction,
|
||||
- generate the transaction using the SDK's `TxBuilder`,
|
||||
- broadcast the transaction using one of the available interfaces.
|
||||
|
||||
Application developers create entrypoints to the application by creating a [command-line interface](../interfaces/cli.md) and/or [REST interface](../interfaces/rest.md), typically found in the application's `./cmd` folder. These interfaces allow users to interact with the application through command-line or through HTTP requests.
|
||||
|
||||
For the [command-line interface](../building-modules/module-interfaces.md#cli), module developers create subcommands to add as children to the application top-level transaction command `TxCmd`. For [HTTP requests](../building-modules/module-interfaces.md#legacy-rest), module developers specify acceptable request types, register REST routes, and create HTTP Request Handlers.
|
||||
|
||||
When users interact with the application's interfaces, they invoke the underlying modules' handlers or command functions, directly creating messages.
|
||||
The next paragraphs will describe each of these components, in this order.
|
||||
|
||||
### Messages
|
||||
|
||||
**`Message`s** are module-specific objects that trigger state transitions within the scope of the module they belong to. Module developers define the `message`s for their module by implementing the `Msg` interface, and also define a [`Handler`](../building-modules/handler.md) to process them.
|
||||
::: tip
|
||||
Module `Msg`s are not to be confused with [ABCI Messages](https://tendermint.com/docs/spec/abci/abci.html#messages) which define interactions between the Tendermint and application layers.
|
||||
:::
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/types/tx_msg.go#L8-L29
|
||||
**Messages** (or `Msg`s) are module-specific objects that trigger state transitions within the scope of the module they belong to. Module developers define the messages for their module by adding methods to the Protobuf [`Msg` service](../building-modules/msg-services.md), and also implement the corresponding `MsgServer`.
|
||||
|
||||
`Message`s in a module are typically defined in a `msgs.go` file (though not always), and one handler with multiple functions to handle each of the module's `message`s is defined in a `handler.go` file.
|
||||
`Msg`s in a module are defined as methods in the [`Msg` service] inside each module's `tx.proto` file. Since `Msg`s are module-specific, each module needs a to process all of its message types and trigger state changes within the module's scope. This design puts more responsibility on module developers, allowing application developers to reuse common functionalities without having to implement state transition logic repetitively. To achieve this, Protobuf generates a `MsgServer` interface for each module, and the module developer needs to implement this interface. The methods on the `MsgServer` interface corresponds on how to handle each of the different `Msg`.
|
||||
|
||||
Note: module `messages` are not to be confused with [ABCI Messages](https://tendermint.com/docs/spec/abci/abci.html#messages) which define interactions between the Tendermint and application layers.
|
||||
|
||||
To learn more about `message`s, click [here](../building-modules/messages-and-queries.md#messages).
|
||||
To learn more about `Msg` services and how to implement `MsgServer`, click [here](../building-modules/msg-services.md).
|
||||
|
||||
While messages contain the information for state transition logic, a transaction's other metadata and relevant information are stored in the `TxBuilder` and `Context`.
|
||||
|
||||
### Transaction Generation
|
||||
|
||||
Transactions are first created by end-users through an `appcli tx` command through the command-line or a POST request to an HTTPS server. For details about transaction creation, click [here](../basics/tx-lifecycle.md#transaction-creation).
|
||||
The `TxBuilder` interface contains data closely related with the generation of transactions, which an end-user can freely set to generate the desired transaction:
|
||||
|
||||
[`Contexts`](https://godoc.org/context) are immutable objects that contain all the information needed to process a request. In the process of creating a transaction through the `auth` module (though it is not mandatory to create transactions this way), two contexts are created: the [`Context`](../interfaces/query-lifecycle.md#context) and `TxBuilder`. Both are automatically generated and do not need to be defined by application developers, but do require input from the transaction creator (e.g. using flags through the CLI).
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/client/tx_config.go#L32-L45
|
||||
|
||||
The `TxBuilder` contains data closely related with the processing of transactions.
|
||||
- `Msg`s, the array of [messages](#messages) included in the transaction.
|
||||
- `GasLimit`, option chosen by the users for how to calculate how much gas they will need to pay.
|
||||
- `Memo`, to send with the transaction.
|
||||
- `FeeAmount`, the maximum amount the user is willing to pay in fees.
|
||||
- `TimeoutHeight`, block height until which the transaction is valid.
|
||||
- `Signatures`, the array of signatures from all signers of the transaction.
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/7d7821b9af132b0f6131640195326aa02b6751db/x/auth/types/txbuilder.go#L18-L31
|
||||
As there are currently two sign modes for signing transactions, there are also two implementations of `TxBuilder`:
|
||||
|
||||
* `TxEncoder` defined by the developer for this type of transaction. Used to encode messages before being processed by nodes running Tendermint.
|
||||
* `Keybase` that manages the user's keys and is used to perform signing operations.
|
||||
* `AccountNumber` from which this transaction originated.
|
||||
* `Sequence`, the number of transactions that the user has sent out, used to prevent replay attacks.
|
||||
* `Gas` option chosen by the users for how to calculate how much gas they will need to pay. A common option is "auto" which generates an automatic estimate.
|
||||
* `GasAdjustment` to adjust the estimate of gas by a scalar value, used to avoid underestimating the amount of gas required.
|
||||
* `SimulateAndExecute` option to simply simulate the transaction execution without broadcasting.
|
||||
* `ChainID` representing which blockchain this transaction pertains to.
|
||||
* `Memo` to send with the transaction.
|
||||
* `Fees`, the maximum amount the user is willing to pay in fees. Alternative to specifying gas prices.
|
||||
* `GasPrices`, the amount per unit of gas the user is willing to pay in fees. Alternative to specifying fees.
|
||||
- [wrapper](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/tx/builder.go#L19-L33) for creating transactions for `SIGN_MODE_DIRECT`,
|
||||
- [StdTxBuilder](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/x/auth/legacy/legacytx/stdtx_builder.go#L14-L20) for `SIGN_MODE_LEGACY_AMINO_JSON`.
|
||||
|
||||
The `Context` is initialized using the application's `codec` and data more closely related to the user interaction with the interface, holding data such as the output to the user and the broadcast mode. Read more about `Context` [here](../interfaces/query-lifecycle.md#context).
|
||||
However, the two implementation of `TxBuilder` should be hidden away from end-users, as they should prefer using the overarching `TxConfig` interface:
|
||||
|
||||
Every message in a transaction must be signed by the addresses specified by `GetSigners`. The signing process must be handled by a module, and the most widely used one is the [`auth`](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec) module. Signing is automatically performed when the transaction is created, unless the user choses to generate and sign separately. The `TxBuilder` (namely, the `KeyBase`) is used to perform the signing operations, and the `Context` is used to broadcast transactions.
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/client/tx_config.go#L21-L30
|
||||
|
||||
### Handlers
|
||||
`TxConfig` is an app-wide configuration for managing transactions. Most importantly, it holds the information about whether to sign each transaction with `SIGN_MODE_DIRECT` or `SIGN_MODE_LEGACY_AMINO_JSON`. By calling `txBuilder := txConfig.NewTxBuilder()`, a new `TxBuilder` will be created with the appropriate sign mode.
|
||||
|
||||
Since `message`s are module-specific types, each module needs a [`handler`](../building-modules/handler.md) to process all of its `message` types and trigger state changes within the module's scope. This design puts more responsibility on module developers, allowing application developers to reuse common functionalities without having to implement state transition logic repetitively. To read more about `handler`s, click [here](../building-modules/handler.md).
|
||||
Once `TxBuilder` is correctly populated with the setters exposed above, `TxConfig` will also take care of correctly encoding the bytes (again, either using `SIGN_MODE_DIRECT` or `SIGN_MODE_LEGACY_AMINO_JSON`). Here's a pseudo-code snippet of how to generate and encode a transaction, using the `TxEncoder()` method:
|
||||
|
||||
```go
|
||||
txBuilder := txConfig.NewTxBuilder()
|
||||
txBuilder.SetMsgs(...) // and other setters on txBuilder
|
||||
|
||||
bz, err := txConfig.TxEncoder()(txBuilder.GetTx())
|
||||
// bz are bytes to be broadcasted over the network
|
||||
```
|
||||
|
||||
### Broadcasting the Transaction
|
||||
|
||||
Once the transaction bytes are generated, there are currently three ways of broadcasting it.
|
||||
|
||||
#### CLI
|
||||
|
||||
Application developers create entrypoints to the application by creating a [command-line interface](../interfaces/cli.md) and/or [REST interface](../interfaces/rest.md), typically found in the application's `./cmd` folder. These interfaces allow users to interact with the application through command-line.
|
||||
|
||||
For the [command-line interface](../building-modules/module-interfaces.md#cli), module developers create subcommands to add as children to the application top-level transaction command `TxCmd`. CLI commands actually bundle all the steps of transaction processing into one simple command: creating messages, generating transactions and broadcasting. For concrete examples, see the [Interacting with a Node](../run-node/interact-node.md) section. An example transaction made using CLI looks like:
|
||||
|
||||
```bash
|
||||
simd tx send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake
|
||||
```
|
||||
|
||||
#### gRPC
|
||||
|
||||
[gRPC](https://grpc.io) is introduced in Cosmos SDK 0.40 as the main component for the SDK's RPC layer. The principal usage of gRPC is in the context of modules' [`Query` services](../building-modules). However, the SDK also exposes a few other module-agnostic gRPC services, one of them being the `Tx` service:
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/proto/cosmos/tx/v1beta1/service.proto
|
||||
|
||||
The `Tx` service exposes a handful of utility functions, such as simulating a transaction or querying a transaction, and also one method to broadcast transactions.
|
||||
|
||||
An example of broadcasting a transaction is shown in [TODO](https://github.com/cosmos/cosmos-sdk/issues/7657). Please note that the `BroadcastTx` endpoint takes `TxRaw`, not bytes.
|
||||
|
||||
#### REST
|
||||
|
||||
Each gRPC method has its corresponding REST endpoint, generated using [gRPC-gateway](https://github.com/grpc-ecosystem/grpc-gateway). Therefore, instead of using gRPC, you can also use HTTP to broadcast the same transaction, on the `POST /cosmos/tx/v1beta1/broadcast_tx` endpoint.
|
||||
|
||||
An example can be seen [here TODO](https://github.com/cosmos/cosmos-sdk/issues/7657)
|
||||
|
||||
## Next {hide}
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ func (k Keeper) OnChanOpenInit(ctx sdk.Context,
|
||||
portID string,
|
||||
channelID string,
|
||||
channelCap *capabilitytypes.Capability,
|
||||
counterParty channeltypes.Counterparty,
|
||||
counterparty channeltypes.Counterparty,
|
||||
version string,
|
||||
) error {
|
||||
// OpenInit must claim the channelCapability that IBC passes into the callback
|
||||
|
||||
@ -87,7 +87,7 @@ Note that **Tendermint only handles transaction bytes**. It has no knowledge of
|
||||
Here are the most important messages of the ABCI:
|
||||
|
||||
- `CheckTx`: When a transaction is received by Tendermint Core, it is passed to the application to check if a few basic requirements are met. `CheckTx` is used to protect the mempool of full-nodes against spam transactions. A special handler called the [`AnteHandler`](../basics/gas-fees.md#antehandler) is used to execute a series of validation steps such as checking for sufficient fees and validating the signatures. If the checks are valid, the transaction is added to the [mempool](https://tendermint.com/docs/spec/reactors/mempool/functionality.html#mempool-functionality) and relayed to peer nodes. Note that transactions are not processed (i.e. no modification of the state occurs) with `CheckTx` since they have not been included in a block yet.
|
||||
- `DeliverTx`: When a [valid block](https://tendermint.com/docs/spec/blockchain/blockchain.html#validation) is received by Tendermint Core, each transaction in the block is passed to the application via `DeliverTx` in order to be processed. It is during this stage that the state transitions occur. The `AnteHandler` executes again along with the actual [`handler`s](../building-modules/handler.md) for each message in the transaction.
|
||||
- `DeliverTx`: When a [valid block](https://tendermint.com/docs/spec/blockchain/blockchain.html#validation) is received by Tendermint Core, each transaction in the block is passed to the application via `DeliverTx` in order to be processed. It is during this stage that the state transitions occur. The `AnteHandler` executes again along with the actual [`Msg` service methods](../building-modules/msg-services.md) for each message in the transaction.
|
||||
- `BeginBlock`/`EndBlock`: These messages are executed at the beginning and the end of each block, whether the block contains transaction or not. It is useful to trigger automatic execution of logic. Proceed with caution though, as computationally expensive loops could slow down your blockchain, or even freeze it if the loop is infinite.
|
||||
|
||||
Find a more detailed view of the ABCI methods from the [Tendermint docs](https://tendermint.com/docs/spec/abci/abci.html#overview).
|
||||
|
||||
94
docs/migrations/rest.md
Normal file
94
docs/migrations/rest.md
Normal file
@ -0,0 +1,94 @@
|
||||
# REST Endpoints Migration
|
||||
|
||||
Migrate your REST endpoints to the Stargate ones. {synopsis}
|
||||
|
||||
## Deprecation of Legacy REST Endpoints
|
||||
|
||||
The Cosmos SDK versions v0.39 and earlier provided REST endpoints to query the state and broadcast transactions. These endpoints are kept in Cosmos SDK v0.40 (Stargate), but they are marked as deprecated, and will be removed in v0.41. We therefore call these endpoints legacy REST endpoints.
|
||||
|
||||
Some important information concerning all legacy REST endpoints:
|
||||
|
||||
- Most of these endpoints are backwards-comptatible. All breaking changes are described in the next section.
|
||||
- In particular, these endpoints still output Amino JSON. Cosmos v0.40 introduced Protobuf as the default encoding library throughout the codebase, but legacy REST endpoints are one of the few places where the encoding is hardcoded to Amino. For more information about Protobuf and Amino, please read our [encoding guide](../core/encoding.md).
|
||||
- All legacy REST endpoints include a [HTTP deprecation header](https://tools.ietf.org/id/draft-dalal-deprecation-header-01.html) which links to this document.
|
||||
|
||||
## Breaking Changes in Legacy REST Endpoints
|
||||
|
||||
| Legacy REST Endpoint | Description | Breaking Change |
|
||||
| ------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `POST /txs` | Query tx by hash | Endpoint will error when trying to broadcast transactions that don't support Amino serialization (e.g. IBC txs)<sup>1</sup>. |
|
||||
| `GET /txs/{hash}` | Query tx by hash | Endpoint will error when trying to output transactions that don't support Amino serialization (e.g. IBC txs)<sup>1</sup>. |
|
||||
| `GET /txs` | Query tx by events | Endpoint will error when trying to output transactions that don't support Amino serialization (e.g. IBC txs)<sup>1</sup>. |
|
||||
| `GET /staking/validators` | Get all validators | BondStatus is now a protobuf enum instead of an int32, and JSON serialized using its protobuf name, so expect query parameters like `?status=BOND_STATUS_{BONDED,UNBONDED,UNBONDING}` as opposed to `?status={bonded,unbonded,unbonding}`. |
|
||||
|
||||
<sup>1</sup>: Transactions that don't support Amino serialization are the ones that contain one or more `Msg`s that are not registered with the Amino codec. Currently in the SDK, only IBC `Msg`s fall into this case.
|
||||
|
||||
## Migrating to New REST Endpoints
|
||||
|
||||
Thanks to the Protocol Buffers migration in v0.40 we are able to take advantage of a vast number of gRPC tools and solutions. For most of the legacy REST endpoints, Cosmos SDK v0.40 provides new REST endpoints generated from [gRPC `Query` services](../building-modules/query-services.md) using [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/). We usually call them _gGPC-gateway REST endpoints_.
|
||||
|
||||
Some modules expose legacy `POST` endpoints to generate unsigned transactions for their `Msg`s. These `POST` endpoints have been removed. We recommend to use [service `Msg`s](../building-modules/msg-services.md) directly, and use Protobuf to do client-side transaction generation. A guide can be found [here (TODO)](https://github.com/cosmos/cosmos-sdk/issues/7657).
|
||||
|
||||
| Legacy REST Endpoint | Description | New gGPC-gateway REST Endpoint |
|
||||
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `GET /txs/{hash}` | Query tx by hash | `GET /cosmos/tx/v1beta1/tx/{hash}` |
|
||||
| `GET /txs` | Query tx by events | `GET /cosmos/tx/v1beta1/txs` |
|
||||
| `POST /txs` | Broadcast tx | `POST /cosmos/tx/v1beta1/txs` |
|
||||
| `POST /txs/encode` | Encodes an Amino JSON tx to an Amino binary tx | N/A, use Protobuf directly |
|
||||
| `POST /txs/decode` | Decodes an Amino binary tx into an Amino JSON tx | N/A, use Protobuf directly |
|
||||
| `POST /bank/*` | Create unsigned `Msg`s for bank tx | N/A, use Protobuf directly |
|
||||
| `GET /bank/balances/{address}` | Get the balance of an address | `GET /cosmos/bank/v1beta1/balances/{address}/{denom}` |
|
||||
| `GET /bank/total` | Get the total supply of all coins | `GET /cosmos/bank/v1beta1/supply` |
|
||||
| `GET /bank/total/{denom}` | Get the total supply of one coin | `GET /cosmos/bank/v1beta1/supply/{denom}` |
|
||||
| `POST /distribution/delegators/{delegatorAddr}/rewards` | Withdraw all delegator rewards | N/A, use Protobuf directly |
|
||||
| `POST /distribution/*` | Create unsigned `Msg`s for distribution | N/A, use Protobuf directly |
|
||||
| `GET /distribution/delegators/{delegatorAddr}/rewards` | Get the total rewards balance from all delegations | `GET /cosmos/distribution/v1beta1/v1beta1/delegators/{delegator_address}/rewards` |
|
||||
| `GET /distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}` | Query a delegation reward | `GET /cosmos/distribution/v1beta1/delegators/{delegatorAddr}/rewards/{validatorAddr}` |
|
||||
| `GET /distribution/delegators/{delegatorAddr}/withdraw_address` | Get the rewards withdrawal address | `GET /cosmos/distribution/v1beta1/delegators/{delegatorAddr}/withdraw_address` |
|
||||
| `GET /distribution/validators/{validatorAddr}` | Validator distribution information | `GET /cosmos/distribution/v1beta1/validators/{validatorAddr}` |
|
||||
| `GET /distribution/validators/{validatorAddr}/rewards` | Commission and self-delegation rewards of a single a validator | `GET /cosmos/distribution/v1beta1/validators/{validatorAddr}/rewards` |
|
||||
| `GET /distribution/validators/{validatorAddr}/outstanding_rewards` | Outstanding rewards of a single validator | `GET /cosmos/distribution/v1beta1/validators/{validatorAddr}/outstanding_rewards` |
|
||||
| `GET /distribution/parameters` | Get the current distribution parameter values | `GET /cosmos/distribution/v1beta1/params` |
|
||||
| `GET /distribution/community_pool` | Get the amount held in the community pool | `GET /cosmos/distribution/v1beta1/community_pool` |
|
||||
| `GET /evidence/{evidence-hash}` | Get evidence by hash | `GET /cosmos/evidence/v1beta1/evidence/{evidence_hash}` |
|
||||
| `GET /evidence` | Get all evidence | `GET /cosmos/evidence/v1beta1/evidence` |
|
||||
| `POST /gov/*` | Create unsigned `Msg`s for gov | N/A, use Protobuf directly |
|
||||
| `GET /gov/parameters/{type}` | Get government parameters | `GET /cosmos/gov/v1beta1/params/{type}` |
|
||||
| `GET /gov/proposals` | Get all proposals | `GET /cosmos/gov/v1beta1/proposals` |
|
||||
| `GET /gov/proposals/{proposal-id}` | Get proposal by id | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}` |
|
||||
| `GET /gov/proposals/{proposal-id}/proposer` | Get proposer of a proposal | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}` (Get proposer from `Proposal` struct) |
|
||||
| `GET /gov/proposals/{proposal-id}/deposits` | Get deposits of a proposal | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}/deposits` |
|
||||
| `GET /gov/proposals/{proposal-id}/deposits/{depositor}` | Get depositor a of deposit | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}/deposits/{depositor}` |
|
||||
| `GET /gov/proposals/{proposal-id}/tally` | Get tally of a proposal | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}/tally` |
|
||||
| `GET /gov/proposals/{proposal-id}/votes` | Get votes of a proposal | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}/votes` |
|
||||
| `GET /gov/proposals/{proposal-id}/votes/{vote}` | Get a particular vote | `GET /cosmos/gov/v1beta1/proposals/{proposal-id}/votes/{vote}` |
|
||||
| `GET /minting/parameters` | Get parameters for minting | `GET /cosmos/minting/v1beta1/params` |
|
||||
| `GET /minting/inflation` | Get minting inflation | `GET /cosmos/minting/v1beta1/inflation` |
|
||||
| `GET /minting/annual-provisions` | Get minting annual provisions | `GET /cosmos/minting/v1beta1/annual_provisions` |
|
||||
| `POST /slashing/*` | Create unsigned `Msg`s for slashing | N/A, use Protobuf directly |
|
||||
| `GET /slashing/validators/{validatorPubKey}/signing_info` | Get validator signing info | `GET /cosmos/slashing/v1beta1/signing_infos/{cons_address}` (Use consensus address instead of pubkey) |
|
||||
| `GET /slashing/signing_infos` | Get all signing infos | `GET /cosmos/slashing/v1beta1/signing_infos` |
|
||||
| `GET /slashing/parameters` | Get slashing parameters | `GET /cosmos/slashing/v1beta1/params` |
|
||||
| `POST /staking/*` | Create unsigned `Msg`s for staking | N/A, use Protobuf directly |
|
||||
| `GET /staking/delegators/{delegatorAddr}/delegations` | Get all delegations from a delegator | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/delegations` |
|
||||
| `GET /staking/delegators/{delegatorAddr}/unbonding_delegations` | Get all unbonding delegations from a delegator | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/unbonding_delegations` |
|
||||
| `GET /staking/delegators/{delegatorAddr}/txs` | Get all staking txs (i.e msgs) from a delegator | Removed |
|
||||
| `GET /staking/delegators/{delegatorAddr}/validators` | Query all validators that a delegator is bonded to | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/validators` |
|
||||
| `GET /staking/delegators/{delegatorAddr}/validators/{validatorAddr}` | Query a validator that a delegator is bonded to | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/validators/{validatorAddr}` |
|
||||
| `GET /staking/delegators/{delegatorAddr}/delegations/{validatorAddr}` | Query a delegation between a delegator and a validator | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/delegations/{validatorAddr}` |
|
||||
| `GET /staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}` | Query all unbonding delegations between a delegator and a validator | `GET /cosmos/staking/v1beta1/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}` |
|
||||
| `GET /staking/redelegations` | Query redelegations | `GET /cosmos/staking/v1beta1/v1beta1/delegators/{delegator_addr}/redelegations` |
|
||||
| `GET /staking/validators` | Get all validators | `GET /cosmos/staking/v1beta1/validators` |
|
||||
| `GET /staking/validators/{validatorAddr}` | Get a single validator info | `GET /cosmos/staking/v1beta1/validators/{validatorAddr}` |
|
||||
| `GET /staking/validators/{validatorAddr}/delegations` | Get all delegations to a validator | `GET /cosmos/staking/v1beta1/validators/{validatorAddr}/delegations` |
|
||||
| `GET /staking/validators/{validatorAddr}/unbonding_delegations` | Get all unbonding delegations from a validator | `GET /cosmos/staking/v1beta1/validators/{validatorAddr}/unbonding_delegations` |
|
||||
| `GET /staking/historical_info/{height}` | Get HistoricalInfo at a given height | `GET /cosmos/staking/v1beta1/historical_info/{height}` |
|
||||
| `GET /staking/pool` | Get the current state of the staking pool | `GET /cosmos/staking/v1beta1/pool` |
|
||||
| `GET /staking/parameters` | Get the current staking parameter values | `GET /cosmos/staking/v1beta1/params` |
|
||||
| `POST /upgrade/*` | Create unsigned `Msg`s for upgrade | N/A, use Protobuf directly |
|
||||
| `GET /upgrade/current` | Get the current plan | `GET /cosmos/upgrade/v1beta1/current_plan` |
|
||||
| `GET /upgrade/applied_plan/{name}` | Get a previously applied plan | `GET /cosmos/upgrade/v1beta1/applied/{name}` |
|
||||
|
||||
## Migrating to gRPC
|
||||
|
||||
Instead of hitting REST endpoints as described in the previous paragraph, the SDK also exposes a gRPC server. Any client can use gRPC instead of REST to interact with the node. An overview of different ways to communicate with a node can be found [here (TODO)](https://github.com/cosmos/cosmos-sdk/issues/7657), and a concrete tutorial for setting up a gRPC client [here (TODO)](https://github.com/cosmos/cosmos-sdk/issues/7657).
|
||||
@ -101,7 +101,7 @@ information.
|
||||
Make sure you can build your own binary, and replace `simd` with the name of your binary in the snippets.
|
||||
:::
|
||||
|
||||
Applications developed using the Cosmos SDK come with the `keys` subcommand. For the purpose of this tutorial, we're running the `simd` CLI, which is an application built using the Cosmos SDK for testing and educational purposes. For more information, see [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc2/simapp).
|
||||
Applications developed using the Cosmos SDK come with the `keys` subcommand. For the purpose of this tutorial, we're running the `simd` CLI, which is an application built using the Cosmos SDK for testing and educational purposes. For more information, see [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc3/simapp).
|
||||
|
||||
You can use `simd keys` for help about the keys command and `simd keys [command] --help` for more information about a particular subcommand.
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ order: 2
|
||||
|
||||
# Running a Node
|
||||
|
||||
Now that the application is ready and the keyring populated, it's time to see how to run the blockchain node. In this section, the application we are running is called [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc2/simapp), and its corresponding CLI binary `simd`. {synopsis}
|
||||
Now that the application is ready and the keyring populated, it's time to see how to run the blockchain node. In this section, the application we are running is called [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc3/simapp), and its corresponding CLI binary `simd`. {synopsis}
|
||||
|
||||
## Pre-requisite Readings
|
||||
|
||||
@ -47,7 +47,7 @@ Now that you have created a local account, go ahead and grant it some `stake` to
|
||||
simd add-genesis-account $MY_VALIDATOR_ADDRESS 100000000stake
|
||||
```
|
||||
|
||||
Recall that `$MY_VALIDATOR_ADDRESS` is a variable that holds the address of the `my_validator` key in the [keyring](./keyring.md#adding-keys-to-the-keyring). Also note that the tokens in the SDK have the `{amount}{denom}` format: `amount` is is a 18-digit-precision decimal number, and `denom` is the unique token identifier with its denomination key (e.g. `atom` or `uatom`). Here, we are granting `stake` tokens, as `stake` is the token identifier used for staking in [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc2/simapp). For your own chain with its own staking denom, that token identifier should be used instead.
|
||||
Recall that `$MY_VALIDATOR_ADDRESS` is a variable that holds the address of the `my_validator` key in the [keyring](./keyring.md#adding-keys-to-the-keyring). Also note that the tokens in the SDK have the `{amount}{denom}` format: `amount` is is a 18-digit-precision decimal number, and `denom` is the unique token identifier with its denomination key (e.g. `atom` or `uatom`). Here, we are granting `stake` tokens, as `stake` is the token identifier used for staking in [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc3/simapp). For your own chain with its own staking denom, that token identifier should be used instead.
|
||||
|
||||
Now that your account has some tokens, you need to add a validator to your chain. Validators are special full-nodes that participate in the consensus process (implemented in the [underlying consensus engine](../intro/sdk-app-architecture.md#tendermint)) in order to add new blocks to the chain. Any account can declare its intention to become a validator operator, but only those with sufficient delegation get to enter the active set (for example, only the top 125 validator candidates with the most delegation get to be validators in the Cosmos Hub). For this guide, you will add your local node (created via the `init` command above) as a validator of your chain. Validators can be declared before a chain is first started via a special transaction included in the genesis file called a `gentx`:
|
||||
|
||||
@ -83,7 +83,7 @@ You should see blocks come in.
|
||||
|
||||
The previous command allow you to run a single node. This is enough for the next section on interacting with this node, but you may wish to run multiple nodes at the same time, and see how consensus happens between them.
|
||||
|
||||
The naive way would be to run the same commands again in separate terminal windows. This is possible, however in the SDK, we leverage the power of [Docker Compose](https://docs.docker.com/compose/) to run a localnet. If you need inspiration on how to set up your own localnet with Docker Compose, you can have a look at the SDK's [`docker-compose.yml`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc2/docker-compose.yml).
|
||||
The naive way would be to run the same commands again in separate terminal windows. This is possible, however in the SDK, we leverage the power of [Docker Compose](https://docs.docker.com/compose/) to run a localnet. If you need inspiration on how to set up your own localnet with Docker Compose, you can have a look at the SDK's [`docker-compose.yml`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/docker-compose.yml).
|
||||
|
||||
## Next {hide}
|
||||
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
master master
|
||||
launchpad/backports v0.39
|
||||
launchpad/backports v0.39
|
||||
|
||||
4
go.mod
4
go.mod
@ -31,7 +31,7 @@ require (
|
||||
github.com/pelletier/go-toml v1.8.0 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.8.0
|
||||
github.com/prometheus/common v0.14.0
|
||||
github.com/prometheus/common v0.15.0
|
||||
github.com/rakyll/statik v0.1.7
|
||||
github.com/regen-network/cosmos-proto v0.3.0
|
||||
github.com/spf13/afero v1.2.2 // indirect
|
||||
@ -44,7 +44,7 @@ require (
|
||||
github.com/tendermint/btcd v0.1.1
|
||||
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15
|
||||
github.com/tendermint/go-amino v0.16.0
|
||||
github.com/tendermint/tendermint v0.34.0-rc6
|
||||
github.com/tendermint/tendermint v0.34.0
|
||||
github.com/tendermint/tm-db v0.6.3
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
|
||||
golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect
|
||||
|
||||
54
go.sum
54
go.sum
@ -4,16 +4,11 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM=
|
||||
@ -24,7 +19,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg=
|
||||
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
|
||||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
|
||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
@ -37,7 +31,6 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI=
|
||||
github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
|
||||
github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@ -50,14 +43,12 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.3.4 h1:Xqf+7f2Vhl9tsqDYmXhnXInUdcrtgpRNpIA15/uldSc=
|
||||
github.com/armon/go-metrics v0.3.4/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.27.0 h1:0xphMHGMLBrPMfxR2AmVjZKcMEESEgWF8Kru94BNByk=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
@ -67,29 +58,21 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw=
|
||||
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0=
|
||||
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M=
|
||||
github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
|
||||
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/goleveldb v1.0.0 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4=
|
||||
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJGQE=
|
||||
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
@ -137,18 +120,14 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8=
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.1 h1:t36VcBCpo4SsmAD5M8wVv1ieVzcALyGfaJ92z4ccULM=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.1/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI=
|
||||
github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
@ -182,11 +161,9 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@ -206,11 +183,9 @@ github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6
|
||||
github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0=
|
||||
github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
@ -229,12 +204,10 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
@ -244,14 +217,12 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
@ -259,7 +230,6 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
@ -281,7 +251,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaD
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.7/go.mod h1:oYZKL012gGh6LMyg/xA7Q2yq6j8bu0wa+9w14EEthWU=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
@ -299,7 +268,6 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
|
||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
@ -312,7 +280,6 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
@ -325,29 +292,24 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
|
||||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
@ -358,7 +320,6 @@ github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNr
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8=
|
||||
github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@ -373,7 +334,6 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
|
||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
@ -394,7 +354,6 @@ github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceT
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
@ -425,13 +384,11 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
@ -480,7 +437,6 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw=
|
||||
github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM=
|
||||
@ -497,16 +453,15 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4=
|
||||
github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM=
|
||||
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
@ -554,7 +509,6 @@ github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
@ -595,9 +549,9 @@ github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM
|
||||
github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
|
||||
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
|
||||
github.com/tendermint/tendermint v0.34.0-rc4/go.mod h1:yotsojf2C1QBOw4dZrTcxbyxmPUrT4hNuOQWX9XUwB4=
|
||||
github.com/tendermint/tendermint v0.34.0-rc6 h1:SVuKGvvE22KxfuK8QUHctUrmOWJsncZSYXIYtcnoKN0=
|
||||
github.com/tendermint/tendermint v0.34.0-rc6/go.mod h1:ugzyZO5foutZImv0Iyx/gOFCX6mjJTgbLHTwi17VDVg=
|
||||
github.com/tendermint/tm-db v0.6.2 h1:DOn8jwCdjJblrCFJbtonEIPD1IuJWpbRUUdR8GWE4RM=
|
||||
github.com/tendermint/tendermint v0.34.0 h1:eXCfMgoqVSzrjzOj6clI9GAejcHH0LvOlRjpCmMJksU=
|
||||
github.com/tendermint/tendermint v0.34.0/go.mod h1:Aj3PIipBFSNO21r+Lq3TtzQ+uKESxkbA3yo/INM4QwQ=
|
||||
github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI=
|
||||
github.com/tendermint/tm-db v0.6.3 h1:ZkhQcKnB8/2jr5EaZwGndN4owkPsGezW2fSisS9zGbg=
|
||||
github.com/tendermint/tm-db v0.6.3/go.mod h1:lfA1dL9/Y/Y8wwyPp2NMLyn5P5Ptr/gvDFNWtrCWSf8=
|
||||
|
||||
135
proto/cosmos/base/tendermint/v1beta1/query.proto
Normal file
135
proto/cosmos/base/tendermint/v1beta1/query.proto
Normal file
@ -0,0 +1,135 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.base.tendermint.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "tendermint/p2p/types.proto";
|
||||
import "tendermint/types/block.proto";
|
||||
import "tendermint/types/types.proto";
|
||||
import "cosmos/base/query/v1beta1/pagination.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/types/query";
|
||||
|
||||
// Service defines the gRPC querier service for tendermint queries.
|
||||
service Service {
|
||||
// GetNodeInfo queries the current node info.
|
||||
rpc GetNodeInfo(GetNodeInfoRequest) returns (GetNodeInfoResponse) {
|
||||
option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/node_info";
|
||||
}
|
||||
// GetSyncing queries node syncing.
|
||||
rpc GetSyncing(GetSyncingRequest) returns (GetSyncingResponse) {
|
||||
option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/syncing";
|
||||
}
|
||||
// GetLatestBlock returns the latest block.
|
||||
rpc GetLatestBlock(GetLatestBlockRequest) returns (GetLatestBlockResponse) {
|
||||
option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/blocks/latest";
|
||||
}
|
||||
// GetBlockByHeight queries block for given height.
|
||||
rpc GetBlockByHeight(GetBlockByHeightRequest) returns (GetBlockByHeightResponse) {
|
||||
option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/blocks/{height}";
|
||||
}
|
||||
|
||||
// GetLatestValidatorSet queries latest validator-set.
|
||||
rpc GetLatestValidatorSet(GetLatestValidatorSetRequest) returns (GetLatestValidatorSetResponse) {
|
||||
option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/validators/latest";
|
||||
}
|
||||
// GetValidatorSetByHeight queries validator-set at a given height.
|
||||
rpc GetValidatorSetByHeight(GetValidatorSetByHeightRequest) returns (GetValidatorSetByHeightResponse) {
|
||||
option (google.api.http).get = "/cosmos/base/tendermint/v1beta1/validators/{height}";
|
||||
}
|
||||
}
|
||||
|
||||
// GetValidatorSetByHeightRequest is the request type for the Query/GetValidatorSetByHeight RPC method.
|
||||
message GetValidatorSetByHeightRequest {
|
||||
int64 height = 1;
|
||||
// pagination defines an pagination for the request.
|
||||
cosmos.base.query.v1beta1.PageRequest pagination = 2;
|
||||
}
|
||||
|
||||
// GetValidatorSetByHeightResponse is the response type for the Query/GetValidatorSetByHeight RPC method.
|
||||
message GetValidatorSetByHeightResponse {
|
||||
int64 block_height = 1;
|
||||
repeated Validator validators = 2;
|
||||
// pagination defines an pagination for the response.
|
||||
cosmos.base.query.v1beta1.PageResponse pagination = 3;
|
||||
}
|
||||
|
||||
// GetLatestValidatorSetRequest is the request type for the Query/GetValidatorSetByHeight RPC method.
|
||||
message GetLatestValidatorSetRequest {
|
||||
// pagination defines an pagination for the request.
|
||||
cosmos.base.query.v1beta1.PageRequest pagination = 1;
|
||||
}
|
||||
|
||||
// GetLatestValidatorSetResponse is the response type for the Query/GetValidatorSetByHeight RPC method.
|
||||
message GetLatestValidatorSetResponse {
|
||||
int64 block_height = 1;
|
||||
repeated Validator validators = 2;
|
||||
// pagination defines an pagination for the response.
|
||||
cosmos.base.query.v1beta1.PageResponse pagination = 3;
|
||||
}
|
||||
|
||||
// Validator is the type for the validator-set.
|
||||
message Validator {
|
||||
bytes address = 1;
|
||||
string pub_key = 2;
|
||||
int64 voting_power = 3;
|
||||
int64 proposer_priority = 4;
|
||||
}
|
||||
|
||||
// GetBlockByHeightRequest is the request type for the Query/GetBlockByHeight RPC method.
|
||||
message GetBlockByHeightRequest {
|
||||
int64 height = 1;
|
||||
}
|
||||
|
||||
// GetBlockByHeightResponse is the response type for the Query/GetBlockByHeight RPC method.
|
||||
message GetBlockByHeightResponse {
|
||||
tendermint.types.BlockID block_id = 1;
|
||||
tendermint.types.Block block = 2;
|
||||
}
|
||||
|
||||
// GetLatestBlockRequest is the request type for the Query/GetLatestBlock RPC method.
|
||||
message GetLatestBlockRequest {}
|
||||
|
||||
// GetLatestBlockResponse is the response type for the Query/GetLatestBlock RPC method.
|
||||
message GetLatestBlockResponse {
|
||||
tendermint.types.BlockID block_id = 1;
|
||||
tendermint.types.Block block = 2;
|
||||
}
|
||||
|
||||
// GetSyncingRequest is the request type for the Query/GetSyncing RPC method.
|
||||
message GetSyncingRequest {}
|
||||
|
||||
// GetSyncingResponse is the response type for the Query/GetSyncing RPC method.
|
||||
message GetSyncingResponse {
|
||||
bool syncing = 1;
|
||||
}
|
||||
|
||||
// GetNodeInfoRequest is the request type for the Query/GetNodeInfo RPC method.
|
||||
message GetNodeInfoRequest {}
|
||||
|
||||
// GetNodeInfoResponse is the request type for the Query/GetNodeInfo RPC method.
|
||||
message GetNodeInfoResponse {
|
||||
tendermint.p2p.DefaultNodeInfo default_node_info = 1;
|
||||
VersionInfo application_version = 2;
|
||||
}
|
||||
|
||||
// VersionInfo is the type for the GetNodeInfoResponse message.
|
||||
message VersionInfo {
|
||||
string name = 1;
|
||||
string app_name = 2;
|
||||
string version = 3;
|
||||
string git_commit = 4;
|
||||
string build_tags = 5;
|
||||
string go_version = 6;
|
||||
repeated Module build_deps = 7;
|
||||
}
|
||||
|
||||
// Module is the type for VersionInfo
|
||||
message Module {
|
||||
// module path
|
||||
string path = 1;
|
||||
// module version
|
||||
string version = 2;
|
||||
// checksum
|
||||
string sum = 3;
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.upgrade.v1beta1;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "cosmos/upgrade/v1beta1/upgrade.proto";
|
||||
|
||||
@ -17,6 +18,14 @@ service Query {
|
||||
rpc AppliedPlan(QueryAppliedPlanRequest) returns (QueryAppliedPlanResponse) {
|
||||
option (google.api.http).get = "/cosmos/upgrade/v1beta1/applied_plan/{name}";
|
||||
}
|
||||
|
||||
// UpgradedConsensusState queries the consensus state that will serve
|
||||
// as a trusted kernel for the next version of this chain. It will only be
|
||||
// stored at the last height of this chain.
|
||||
// UpgradedConsensusState RPC not supported with legacy querier
|
||||
rpc UpgradedConsensusState(QueryUpgradedConsensusStateRequest) returns (QueryUpgradedConsensusStateResponse) {
|
||||
option (google.api.http).get = "/cosmos/upgrade/v1beta1/upgraded_consensus_state/{last_height}";
|
||||
}
|
||||
}
|
||||
|
||||
// QueryCurrentPlanRequest is the request type for the Query/CurrentPlan RPC
|
||||
@ -43,3 +52,17 @@ message QueryAppliedPlanResponse {
|
||||
// height is the block height at which the plan was applied.
|
||||
int64 height = 1;
|
||||
}
|
||||
|
||||
// QueryUpgradedConsensusStateRequest is the request type for the Query/UpgradedConsensusState
|
||||
// RPC method.
|
||||
message QueryUpgradedConsensusStateRequest {
|
||||
// last height of the current chain must be sent in request
|
||||
// as this is the height under which next consensus state is stored
|
||||
int64 last_height = 1;
|
||||
}
|
||||
|
||||
// QueryUpgradedConsensusStateResponse is the response type for the Query/UpgradedConsensusState
|
||||
// RPC method.
|
||||
message QueryUpgradedConsensusStateResponse {
|
||||
google.protobuf.Any upgraded_consensus_state = 1;
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ message GenesisState {
|
||||
[(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"recv_sequences\""];
|
||||
repeated PacketSequence ack_sequences = 7
|
||||
[(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"ack_sequences\""];
|
||||
// the sequence for the next generated channel identifier
|
||||
uint64 next_channel_sequence = 8 [(gogoproto.moretags) = "yaml:\"next_channel_sequence\""];
|
||||
}
|
||||
|
||||
// PacketSequence defines the genesis type necessary to retrieve and store
|
||||
|
||||
@ -38,8 +38,8 @@ service Query {
|
||||
// associated with the provided channel identifiers.
|
||||
rpc ChannelConsensusState(QueryChannelConsensusStateRequest) returns (QueryChannelConsensusStateResponse) {
|
||||
option (google.api.http).get =
|
||||
"/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/consensus_state/version/"
|
||||
"{version_number}/height/{version_height}";
|
||||
"/ibc/core/channel/v1beta1/channels/{channel_id}/ports/{port_id}/consensus_state/revision/"
|
||||
"{revision_number}/height/{revision_height}";
|
||||
}
|
||||
|
||||
// PacketCommitment queries a stored packet commitment hash.
|
||||
@ -176,10 +176,10 @@ message QueryChannelConsensusStateRequest {
|
||||
string port_id = 1;
|
||||
// channel unique identifier
|
||||
string channel_id = 2;
|
||||
// version number of the consensus state
|
||||
uint64 version_number = 3;
|
||||
// version height of the consensus state
|
||||
uint64 version_height = 4;
|
||||
// revision number of the consensus state
|
||||
uint64 revision_number = 3;
|
||||
// revision height of the consensus state
|
||||
uint64 revision_height = 4;
|
||||
}
|
||||
|
||||
// QueryChannelClientStateResponse is the Response type for the
|
||||
|
||||
@ -46,10 +46,9 @@ message MsgChannelOpenInit {
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""];
|
||||
string channel_id = 2 [(gogoproto.moretags) = "yaml:\"channel_id\""];
|
||||
Channel channel = 3 [(gogoproto.nullable) = false];
|
||||
string signer = 4;
|
||||
string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""];
|
||||
Channel channel = 2 [(gogoproto.nullable) = false];
|
||||
string signer = 3;
|
||||
}
|
||||
|
||||
// MsgChannelOpenInitResponse defines the Msg/ChannelOpenInit response type.
|
||||
@ -61,15 +60,16 @@ message MsgChannelOpenTry {
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""];
|
||||
string desired_channel_id = 2 [(gogoproto.moretags) = "yaml:\"desired_channel_id\""];
|
||||
string counterparty_chosen_channel_id = 3 [(gogoproto.moretags) = "yaml:\"counterparty_chosen_channel_id\""];
|
||||
Channel channel = 4 [(gogoproto.nullable) = false];
|
||||
string counterparty_version = 5 [(gogoproto.moretags) = "yaml:\"counterparty_version\""];
|
||||
bytes proof_init = 6 [(gogoproto.moretags) = "yaml:\"proof_init\""];
|
||||
ibc.core.client.v1.Height proof_height = 7
|
||||
string port_id = 1 [(gogoproto.moretags) = "yaml:\"port_id\""];
|
||||
// in the case of crossing hello's, when both chains call OpenInit, we need the channel identifier
|
||||
// of the previous channel in state INIT
|
||||
string previous_channel_id = 2 [(gogoproto.moretags) = "yaml:\"previous_channel_id\""];
|
||||
Channel channel = 3 [(gogoproto.nullable) = false];
|
||||
string counterparty_version = 4 [(gogoproto.moretags) = "yaml:\"counterparty_version\""];
|
||||
bytes proof_init = 5 [(gogoproto.moretags) = "yaml:\"proof_init\""];
|
||||
ibc.core.client.v1.Height proof_height = 6
|
||||
[(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false];
|
||||
string signer = 8;
|
||||
string signer = 7;
|
||||
}
|
||||
|
||||
// MsgChannelOpenTryResponse defines the Msg/ChannelOpenTry response type.
|
||||
@ -147,9 +147,9 @@ message MsgRecvPacket {
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
Packet packet = 1 [(gogoproto.nullable) = false];
|
||||
bytes proof_commitment = 2 [(gogoproto.moretags) = "yaml:\"proof_commitment\""];
|
||||
ibc.core.client.v1.Height proof_height = 3
|
||||
Packet packet = 1 [(gogoproto.nullable) = false];
|
||||
bytes proof_commitment = 2 [(gogoproto.moretags) = "yaml:\"proof_commitment\""];
|
||||
ibc.core.client.v1.Height proof_height = 3
|
||||
[(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false];
|
||||
string signer = 4;
|
||||
}
|
||||
@ -162,9 +162,9 @@ message MsgTimeout {
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
Packet packet = 1 [(gogoproto.nullable) = false];
|
||||
bytes proof_unreceived = 2 [(gogoproto.moretags) = "yaml:\"proof_unreceived\""];
|
||||
ibc.core.client.v1.Height proof_height = 3
|
||||
Packet packet = 1 [(gogoproto.nullable) = false];
|
||||
bytes proof_unreceived = 2 [(gogoproto.moretags) = "yaml:\"proof_unreceived\""];
|
||||
ibc.core.client.v1.Height proof_height = 3
|
||||
[(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false];
|
||||
uint64 next_sequence_recv = 4 [(gogoproto.moretags) = "yaml:\"next_sequence_recv\""];
|
||||
string signer = 5;
|
||||
@ -178,10 +178,10 @@ message MsgTimeoutOnClose {
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
Packet packet = 1 [(gogoproto.nullable) = false];
|
||||
bytes proof_unreceived = 2 [(gogoproto.moretags) = "yaml:\"proof_unreceived\""];
|
||||
bytes proof_close = 3 [(gogoproto.moretags) = "yaml:\"proof_close\""];
|
||||
ibc.core.client.v1.Height proof_height = 4
|
||||
Packet packet = 1 [(gogoproto.nullable) = false];
|
||||
bytes proof_unreceived = 2 [(gogoproto.moretags) = "yaml:\"proof_unreceived\""];
|
||||
bytes proof_close = 3 [(gogoproto.moretags) = "yaml:\"proof_close\""];
|
||||
ibc.core.client.v1.Height proof_height = 4
|
||||
[(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false];
|
||||
uint64 next_sequence_recv = 5 [(gogoproto.moretags) = "yaml:\"next_sequence_recv\""];
|
||||
string signer = 6;
|
||||
@ -197,7 +197,7 @@ message MsgAcknowledgement {
|
||||
|
||||
Packet packet = 1 [(gogoproto.nullable) = false];
|
||||
bytes acknowledgement = 2;
|
||||
bytes proof_acked = 3 [(gogoproto.moretags) = "yaml:\"proof_acked\""];
|
||||
bytes proof_acked = 3 [(gogoproto.moretags) = "yaml:\"proof_acked\""];
|
||||
ibc.core.client.v1.Height proof_height = 4
|
||||
[(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false];
|
||||
string signer = 5;
|
||||
|
||||
@ -52,19 +52,19 @@ message ClientUpdateProposal {
|
||||
// that can be compared against another Height for the purposes of updating and
|
||||
// freezing clients
|
||||
//
|
||||
// Normally the VersionHeight is incremented at each height while keeping version
|
||||
// number the same However some consensus algorithms may choose to reset the
|
||||
// Normally the RevisionHeight is incremented at each height while keeping RevisionNumber
|
||||
// the same. However some consensus algorithms may choose to reset the
|
||||
// height in certain conditions e.g. hard forks, state-machine breaking changes
|
||||
// In these cases, the version number is incremented so that height continues to
|
||||
// be monitonically increasing even as the VersionHeight gets reset
|
||||
// In these cases, the RevisionNumber is incremented so that height continues to
|
||||
// be monitonically increasing even as the RevisionHeight gets reset
|
||||
message Height {
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// the version that the client is currently on
|
||||
uint64 version_number = 1 [(gogoproto.moretags) = "yaml:\"version_number\""];
|
||||
// the height within the given version
|
||||
uint64 version_height = 2 [(gogoproto.moretags) = "yaml:\"version_height\""];
|
||||
// the revision that the client is currently on
|
||||
uint64 revision_number = 1 [(gogoproto.moretags) = "yaml:\"revision_number\""];
|
||||
// the height within the given revision
|
||||
uint64 revision_height = 2 [(gogoproto.moretags) = "yaml:\"revision_height\""];
|
||||
}
|
||||
|
||||
// Params defines the set of IBC light client parameters.
|
||||
|
||||
@ -24,8 +24,8 @@ service Query {
|
||||
// ConsensusState queries a consensus state associated with a client state at
|
||||
// a given height.
|
||||
rpc ConsensusState(QueryConsensusStateRequest) returns (QueryConsensusStateResponse) {
|
||||
option (google.api.http).get = "/ibc/core/client/v1beta1/consensus_states/{client_id}/version/{version_number}/"
|
||||
"height/{version_height}";
|
||||
option (google.api.http).get = "/ibc/core/client/v1beta1/consensus_states/{client_id}/revision/{revision_number}/"
|
||||
"height/{revision_height}";
|
||||
}
|
||||
|
||||
// ConsensusStates queries all the consensus state associated with a given
|
||||
@ -82,10 +82,10 @@ message QueryClientStatesResponse {
|
||||
message QueryConsensusStateRequest {
|
||||
// client identifier
|
||||
string client_id = 1;
|
||||
// consensus state version number
|
||||
uint64 version_number = 2;
|
||||
// consensus state version height
|
||||
uint64 version_height = 3;
|
||||
// consensus state revision number
|
||||
uint64 revision_number = 2;
|
||||
// consensus state revision height
|
||||
uint64 revision_height = 3;
|
||||
// latest_height overrrides the height field and queries the latest stored
|
||||
// ConsensusState
|
||||
bool latest_height = 4;
|
||||
|
||||
@ -60,16 +60,21 @@ message MsgUpdateClientResponse {}
|
||||
|
||||
// MsgUpgradeClient defines an sdk.Msg to upgrade an IBC client to a new client state
|
||||
message MsgUpgradeClient {
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
// client unique identifier
|
||||
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
|
||||
// upgraded client state
|
||||
google.protobuf.Any client_state = 2 [(gogoproto.moretags) = "yaml:\"client_state\""];
|
||||
// height at which old chain halts and upgrades (i.e last block executed)
|
||||
Height upgrade_height = 3 [(gogoproto.moretags) = "yaml:\"upgrade_height\""];
|
||||
// upgraded consensus state, only contains enough information to serve as a basis of trust in update logic
|
||||
google.protobuf.Any consensus_state = 3 [(gogoproto.moretags) = "yaml:\"consensus_state\""];
|
||||
// proof that old chain committed to new client
|
||||
bytes proof_upgrade = 4 [(gogoproto.moretags) = "yaml:\"proof_upgrade\""];
|
||||
bytes proof_upgrade_client = 4 [(gogoproto.moretags) = "yaml:\"proof_upgrade_client\""];
|
||||
// proof that old chain committed to new consensus state
|
||||
bytes proof_upgrade_consensus_state = 5 [(gogoproto.moretags) = "yaml:\"proof_upgrade_consensus_state\""];
|
||||
// signer address
|
||||
string signer = 5;
|
||||
string signer = 6;
|
||||
}
|
||||
|
||||
// MsgUpgradeClientResponse defines the Msg/UpgradeClient response type.
|
||||
|
||||
@ -11,4 +11,6 @@ message GenesisState {
|
||||
repeated IdentifiedConnection connections = 1 [(gogoproto.nullable) = false];
|
||||
repeated ConnectionPaths client_connection_paths = 2
|
||||
[(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"client_connection_paths\""];
|
||||
// the sequence for the next generated connection identifier
|
||||
uint64 next_connection_sequence = 3 [(gogoproto.moretags) = "yaml:\"next_connection_sequence\""];
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ service Query {
|
||||
// connection.
|
||||
rpc ConnectionConsensusState(QueryConnectionConsensusStateRequest) returns (QueryConnectionConsensusStateResponse) {
|
||||
option (google.api.http).get = "/ibc/core/connection/v1beta1/connections/{connection_id}/consensus_state/"
|
||||
"version/{version_number}/height/{version_height}";
|
||||
"revision/{revision_number}/height/{revision_height}";
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,9 +118,9 @@ message QueryConnectionClientStateResponse {
|
||||
// Query/ConnectionConsensusState RPC method
|
||||
message QueryConnectionConsensusStateRequest {
|
||||
// connection identifier
|
||||
string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""];
|
||||
uint64 version_number = 2;
|
||||
uint64 version_height = 3;
|
||||
string connection_id = 1 [(gogoproto.moretags) = "yaml:\"connection_id\""];
|
||||
uint64 revision_number = 2;
|
||||
uint64 revision_height = 3;
|
||||
}
|
||||
|
||||
// QueryConnectionConsensusStateResponse is the response type for the
|
||||
|
||||
@ -29,11 +29,10 @@ message MsgConnectionOpenInit {
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
|
||||
string connection_id = 2 [(gogoproto.moretags) = "yaml:\"connection_id\""];
|
||||
Counterparty counterparty = 3 [(gogoproto.nullable) = false];
|
||||
Version version = 4;
|
||||
string signer = 5;
|
||||
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
|
||||
Counterparty counterparty = 2 [(gogoproto.nullable) = false];
|
||||
Version version = 3;
|
||||
string signer = 4;
|
||||
}
|
||||
|
||||
// MsgConnectionOpenInitResponse defines the Msg/ConnectionOpenInit response type.
|
||||
@ -45,24 +44,25 @@ message MsgConnectionOpenTry {
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
|
||||
string desired_connection_id = 2 [(gogoproto.moretags) = "yaml:\"desired_connection_id\""];
|
||||
string counterparty_chosen_connection_id = 3 [(gogoproto.moretags) = "yaml:\"counterparty_chosen_connection_id\""];
|
||||
google.protobuf.Any client_state = 4 [(gogoproto.moretags) = "yaml:\"client_state\""];
|
||||
Counterparty counterparty = 5 [(gogoproto.nullable) = false];
|
||||
repeated Version counterparty_versions = 6 [(gogoproto.moretags) = "yaml:\"counterparty_versions\""];
|
||||
ibc.core.client.v1.Height proof_height = 7
|
||||
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
|
||||
// in the case of crossing hello's, when both chains call OpenInit, we need the connection identifier
|
||||
// of the previous connection in state INIT
|
||||
string previous_connection_id = 2 [(gogoproto.moretags) = "yaml:\"previous_connection_id\""];
|
||||
google.protobuf.Any client_state = 3 [(gogoproto.moretags) = "yaml:\"client_state\""];
|
||||
Counterparty counterparty = 4 [(gogoproto.nullable) = false];
|
||||
repeated Version counterparty_versions = 5 [(gogoproto.moretags) = "yaml:\"counterparty_versions\""];
|
||||
ibc.core.client.v1.Height proof_height = 6
|
||||
[(gogoproto.moretags) = "yaml:\"proof_height\"", (gogoproto.nullable) = false];
|
||||
// proof of the initialization the connection on Chain A: `UNITIALIZED ->
|
||||
// INIT`
|
||||
bytes proof_init = 8 [(gogoproto.moretags) = "yaml:\"proof_init\""];
|
||||
bytes proof_init = 7 [(gogoproto.moretags) = "yaml:\"proof_init\""];
|
||||
// proof of client state included in message
|
||||
bytes proof_client = 9 [(gogoproto.moretags) = "yaml:\"proof_client\""];
|
||||
bytes proof_client = 8 [(gogoproto.moretags) = "yaml:\"proof_client\""];
|
||||
// proof of client consensus state
|
||||
bytes proof_consensus = 10 [(gogoproto.moretags) = "yaml:\"proof_consensus\""];
|
||||
ibc.core.client.v1.Height consensus_height = 11
|
||||
bytes proof_consensus = 9 [(gogoproto.moretags) = "yaml:\"proof_consensus\""];
|
||||
ibc.core.client.v1.Height consensus_height = 10
|
||||
[(gogoproto.moretags) = "yaml:\"consensus_height\"", (gogoproto.nullable) = false];
|
||||
string signer = 12;
|
||||
string signer = 11;
|
||||
}
|
||||
|
||||
// MsgConnectionOpenTryResponse defines the Msg/ConnectionOpenTry response type.
|
||||
|
||||
@ -48,11 +48,11 @@ message Header {
|
||||
// Misbehaviour defines misbehaviour for a solo machine which consists
|
||||
// of a sequence and two signatures over different messages at that sequence.
|
||||
message Misbehaviour {
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
|
||||
uint64 sequence = 2;
|
||||
SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""];
|
||||
SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""];
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
|
||||
uint64 sequence = 2;
|
||||
SignatureAndData signature_one = 3 [(gogoproto.moretags) = "yaml:\"signature_one\""];
|
||||
SignatureAndData signature_two = 4 [(gogoproto.moretags) = "yaml:\"signature_two\""];
|
||||
}
|
||||
|
||||
// SignatureAndData contains a signature and the data signed over to create that
|
||||
|
||||
@ -42,8 +42,12 @@ message ClientState {
|
||||
// Proof specifications used in verifying counterparty state
|
||||
repeated ics23.ProofSpec proof_specs = 8 [(gogoproto.moretags) = "yaml:\"proof_specs\""];
|
||||
|
||||
// Path at which next upgraded client will be committed
|
||||
string upgrade_path = 9 [(gogoproto.moretags) = "yaml:\"upgrade_path\""];
|
||||
// Path at which next upgraded client will be committed.
|
||||
// Each element corresponds to the key for a single CommitmentProof in the chained proof.
|
||||
// NOTE: ClientState must stored under `{upgradePath}/{upgradeHeight}/clientState`
|
||||
// ConsensusState must be stored under `{upgradepath}/{upgradeHeight}/consensusState`
|
||||
// For SDK chains using the default upgrade module, upgrade_path should be []string{"upgrade", "upgradedIBCState"}`
|
||||
repeated string upgrade_path = 9 [(gogoproto.moretags) = "yaml:\"upgrade_path\""];
|
||||
|
||||
// This flag, when set to true, will allow governance to recover a client
|
||||
// which has expired
|
||||
@ -71,7 +75,7 @@ message ConsensusState {
|
||||
// Misbehaviour is a wrapper over two conflicting Headers
|
||||
// that implements Misbehaviour interface expected by ICS-02
|
||||
message Misbehaviour {
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
string client_id = 1 [(gogoproto.moretags) = "yaml:\"client_id\""];
|
||||
Header header_1 = 2 [(gogoproto.customname) = "Header1", (gogoproto.moretags) = "yaml:\"header_1\""];
|
||||
|
||||
@ -258,10 +258,10 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App
|
||||
// service if API or gRPC is enabled, and avoid doing so in the general
|
||||
// case, because it spawns a new local tendermint RPC client.
|
||||
if config.API.Enable || config.GRPC.Enable {
|
||||
clientCtx = clientCtx.
|
||||
WithClient(local.New(tmNode))
|
||||
clientCtx = clientCtx.WithClient(local.New(tmNode))
|
||||
|
||||
app.RegisterTxService(clientCtx)
|
||||
app.RegisterTendermintService(clientCtx)
|
||||
}
|
||||
|
||||
var apiSrv *api.Server
|
||||
|
||||
@ -43,6 +43,9 @@ type (
|
||||
// RegisterTxService registers the gRPC Query service for tx (such as tx
|
||||
// simulation, fetching txs by hash...).
|
||||
RegisterTxService(clientCtx client.Context)
|
||||
|
||||
// RegisterTendermintService registers the gRPC Query service for tendermint queries.
|
||||
RegisterTendermintService(clientCtx client.Context)
|
||||
}
|
||||
|
||||
// AppCreator is a function that allows us to lazily initialize an
|
||||
|
||||
@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/grpc/tmservice"
|
||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
@ -560,6 +561,8 @@ func (app *SimApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APICon
|
||||
authrest.RegisterTxRoutes(clientCtx, apiSvr.Router)
|
||||
// Register new tx routes from grpc-gateway.
|
||||
authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCRouter)
|
||||
// Register new tendermint queries routes from grpc-gateway.
|
||||
tmservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCRouter)
|
||||
|
||||
// Register legacy and grpc-gateway routes for all modules.
|
||||
ModuleBasics.RegisterRESTRoutes(clientCtx, apiSvr.Router)
|
||||
@ -576,6 +579,11 @@ func (app *SimApp) RegisterTxService(clientCtx client.Context) {
|
||||
authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry)
|
||||
}
|
||||
|
||||
// RegisterTendermintService implements the Application.RegisterTendermintService method.
|
||||
func (app *SimApp) RegisterTendermintService(clientCtx client.Context) {
|
||||
tmservice.RegisterTendermintService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.interfaceRegistry)
|
||||
}
|
||||
|
||||
// RegisterSwaggerAPI registers swagger route with API Server
|
||||
func RegisterSwaggerAPI(ctx client.Context, rtr *mux.Router) {
|
||||
statikFS, err := fs.New()
|
||||
|
||||
@ -67,7 +67,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
|
||||
addr = info.GetAddress()
|
||||
}
|
||||
|
||||
coins, err := sdk.ParseCoins(args[1])
|
||||
coins, err := sdk.ParseCoinsNormalized(args[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse coins: %w", err)
|
||||
}
|
||||
@ -76,7 +76,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
|
||||
vestingEnd, _ := cmd.Flags().GetInt64(flagVestingEnd)
|
||||
vestingAmtStr, _ := cmd.Flags().GetString(flagVestingAmt)
|
||||
|
||||
vestingAmt, err := sdk.ParseCoins(vestingAmtStr)
|
||||
vestingAmt, err := sdk.ParseCoinsNormalized(vestingAmtStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse vesting amount: %w", err)
|
||||
}
|
||||
|
||||
@ -167,6 +167,7 @@ func txCommand() *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// newApp is an AppCreator
|
||||
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application {
|
||||
var cache sdk.MultiStorePersistentCache
|
||||
|
||||
|
||||
@ -66,6 +66,9 @@ func startInProcess(cfg Config, val *Validator) error {
|
||||
|
||||
// Add the tx service in the gRPC router.
|
||||
app.RegisterTxService(val.ClientCtx)
|
||||
|
||||
// Add the tendermint queries service in the gRPC router.
|
||||
app.RegisterTendermintService(val.ClientCtx)
|
||||
}
|
||||
|
||||
if val.APIAddress != "" {
|
||||
|
||||
34
third_party/proto/tendermint/p2p/types.proto
vendored
Normal file
34
third_party/proto/tendermint/p2p/types.proto
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
syntax = "proto3";
|
||||
package tendermint.p2p;
|
||||
|
||||
option go_package = "github.com/tendermint/tendermint/proto/tendermint/p2p";
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
|
||||
message NetAddress {
|
||||
string id = 1 [(gogoproto.customname) = "ID"];
|
||||
string ip = 2 [(gogoproto.customname) = "IP"];
|
||||
uint32 port = 3;
|
||||
}
|
||||
|
||||
message ProtocolVersion {
|
||||
uint64 p2p = 1 [(gogoproto.customname) = "P2P"];
|
||||
uint64 block = 2;
|
||||
uint64 app = 3;
|
||||
}
|
||||
|
||||
message DefaultNodeInfo {
|
||||
ProtocolVersion protocol_version = 1 [(gogoproto.nullable) = false];
|
||||
string default_node_id = 2 [(gogoproto.customname) = "DefaultNodeID"];
|
||||
string listen_addr = 3;
|
||||
string network = 4;
|
||||
string version = 5;
|
||||
bytes channels = 6;
|
||||
string moniker = 7;
|
||||
DefaultNodeInfoOther other = 8 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
message DefaultNodeInfoOther {
|
||||
string tx_index = 1;
|
||||
string rpc_address = 2 [(gogoproto.customname) = "RPCAddress"];
|
||||
}
|
||||
15
third_party/proto/tendermint/types/block.proto
vendored
Normal file
15
third_party/proto/tendermint/types/block.proto
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
syntax = "proto3";
|
||||
package tendermint.types;
|
||||
|
||||
option go_package = "github.com/tendermint/tendermint/proto/tendermint/types";
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "tendermint/types/types.proto";
|
||||
import "tendermint/types/evidence.proto";
|
||||
|
||||
message Block {
|
||||
Header header = 1 [(gogoproto.nullable) = false];
|
||||
Data data = 2 [(gogoproto.nullable) = false];
|
||||
tendermint.types.EvidenceList evidence = 3 [(gogoproto.nullable) = false];
|
||||
Commit last_commit = 4;
|
||||
}
|
||||
@ -31,15 +31,15 @@ const (
|
||||
|
||||
// AddrLen defines a valid address length
|
||||
AddrLen = 20
|
||||
// Bech32PrefixAccAddr defines the Bech32 prefix of an account's address
|
||||
// Bech32MainPrefix defines the main SDK Bech32 prefix of an account's address
|
||||
Bech32MainPrefix = "cosmos"
|
||||
|
||||
// Atom in https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||
// CoinType is the ATOM coin type as defined in SLIP44 (https://github.com/satoshilabs/slips/blob/master/slip-0044.md)
|
||||
CoinType = 118
|
||||
|
||||
// BIP44Prefix is the parts of the BIP44 HD path that are fixed by
|
||||
// what we used during the fundraiser.
|
||||
FullFundraiserPath = "44'/118'/0'/0/0"
|
||||
// FullFundraiserPath is the parts of the BIP44 HD path that are fixed by
|
||||
// what we used during the ATOM fundraiser.
|
||||
FullFundraiserPath = "m/44'/118'/0'/0/0"
|
||||
|
||||
// PrefixAccount is the prefix for account keys
|
||||
PrefixAccount = "acc"
|
||||
|
||||
30
types/bench_test.go
Normal file
30
types/bench_test.go
Normal file
@ -0,0 +1,30 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var coinStrs = []string{
|
||||
"2000ATM",
|
||||
"5000AMX",
|
||||
"192XXX",
|
||||
"1e9BTC",
|
||||
}
|
||||
|
||||
func BenchmarkParseCoin(b *testing.B) {
|
||||
var blankCoin types.Coin
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, coinStr := range coinStrs {
|
||||
coin, err := types.ParseCoinNormalized(coinStr)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if coin == blankCoin {
|
||||
b.Fatal("Unexpectedly returned a blank coin")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -599,22 +599,14 @@ var (
|
||||
// Denominations can be 3 ~ 128 characters long and support letters, followed by either
|
||||
// a letter, a number or a separator ('/').
|
||||
reDnmString = `[a-zA-Z][a-zA-Z0-9/]{2,127}`
|
||||
reAmt = `[[:digit:]]+`
|
||||
reDecAmt = `[[:digit:]]*\.[[:digit:]]+`
|
||||
reDecAmt = `[[:digit:]]+(?:\.[[:digit:]]+)?|\.[[:digit:]]+`
|
||||
reSpc = `[[:space:]]*`
|
||||
reDnm = returnReDnm
|
||||
reCoin = returnReCoin
|
||||
reDecCoin = returnDecCoin
|
||||
reDnm *regexp.Regexp
|
||||
reDecCoin *regexp.Regexp
|
||||
)
|
||||
|
||||
func returnDecCoin() *regexp.Regexp {
|
||||
return regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reDecAmt, reSpc, CoinDenomRegex()))
|
||||
}
|
||||
func returnReCoin() *regexp.Regexp {
|
||||
return regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, CoinDenomRegex()))
|
||||
}
|
||||
func returnReDnm() *regexp.Regexp {
|
||||
return regexp.MustCompile(fmt.Sprintf(`^%s$`, CoinDenomRegex()))
|
||||
func init() {
|
||||
SetCoinDenomRegex(DefaultCoinDenomRegex)
|
||||
}
|
||||
|
||||
// DefaultCoinDenomRegex returns the default regex string
|
||||
@ -622,12 +614,21 @@ func DefaultCoinDenomRegex() string {
|
||||
return reDnmString
|
||||
}
|
||||
|
||||
// CoinDenomRegex returns the current regex string and can be overwritten for custom validation
|
||||
var CoinDenomRegex = DefaultCoinDenomRegex
|
||||
// coinDenomRegex returns the current regex string and can be overwritten for custom validation
|
||||
var coinDenomRegex = DefaultCoinDenomRegex
|
||||
|
||||
// SetCoinDenomRegex allows for coin's custom validation by overriding the regular
|
||||
// expression string used for denom validation.
|
||||
func SetCoinDenomRegex(reFn func() string) {
|
||||
coinDenomRegex = reFn
|
||||
|
||||
reDnm = regexp.MustCompile(fmt.Sprintf(`^%s$`, coinDenomRegex()))
|
||||
reDecCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reDecAmt, reSpc, coinDenomRegex()))
|
||||
}
|
||||
|
||||
// ValidateDenom is the default validation function for Coin.Denom.
|
||||
func ValidateDenom(denom string) error {
|
||||
if !reDnm().MatchString(denom) {
|
||||
if !reDnm.MatchString(denom) {
|
||||
return fmt.Errorf("invalid denom: %s", denom)
|
||||
}
|
||||
return nil
|
||||
@ -639,58 +640,31 @@ func mustValidateDenom(denom string) {
|
||||
}
|
||||
}
|
||||
|
||||
// ParseCoin parses a cli input for one coin type, returning errors if invalid or on an empty string
|
||||
// ParseCoinNormalized parses and normalize a cli input for one coin type, returning errors if invalid or on an empty string
|
||||
// as well.
|
||||
// Expected format: "{amount}{denomination}"
|
||||
func ParseCoin(coinStr string) (coin Coin, err error) {
|
||||
coinStr = strings.TrimSpace(coinStr)
|
||||
|
||||
matches := reCoin().FindStringSubmatch(coinStr)
|
||||
if matches == nil {
|
||||
return Coin{}, fmt.Errorf("invalid coin expression: %s", coinStr)
|
||||
}
|
||||
|
||||
denomStr, amountStr := matches[2], matches[1]
|
||||
|
||||
amount, ok := NewIntFromString(amountStr)
|
||||
if !ok {
|
||||
return Coin{}, fmt.Errorf("failed to parse coin amount: %s", amountStr)
|
||||
}
|
||||
|
||||
if err := ValidateDenom(denomStr); err != nil {
|
||||
func ParseCoinNormalized(coinStr string) (coin Coin, err error) {
|
||||
decCoin, err := ParseDecCoin(coinStr)
|
||||
if err != nil {
|
||||
return Coin{}, err
|
||||
}
|
||||
|
||||
return NewCoin(denomStr, amount), nil
|
||||
coin, _ = NormalizeDecCoin(decCoin).TruncateDecimal()
|
||||
return coin, nil
|
||||
}
|
||||
|
||||
// ParseCoins will parse out a list of coins separated by commas. If the parsing is successuful,
|
||||
// the provided coins will be sanitized by removing zero coins and sorting the coin set. Lastly
|
||||
// a validation of the coin set is executed. If the check passes, ParseCoins will return the sanitized coins.
|
||||
// ParseCoinsNormalized will parse out a list of coins separated by commas, and normalize them by converting to smallest
|
||||
// unit. If the parsing is successuful, the provided coins will be sanitized by removing zero coins and sorting the coin
|
||||
// set. Lastly a validation of the coin set is executed. If the check passes, ParseCoinsNormalized will return the
|
||||
// sanitized coins.
|
||||
// Otherwise it will return an error.
|
||||
// If an empty string is provided to ParseCoins, it returns nil Coins.
|
||||
// If an empty string is provided to ParseCoinsNormalized, it returns nil Coins.
|
||||
// ParseCoinsNormalized supports decimal coins as inputs, and truncate them to int after converted to smallest unit.
|
||||
// Expected format: "{amount0}{denomination},...,{amountN}{denominationN}"
|
||||
func ParseCoins(coinsStr string) (Coins, error) {
|
||||
coinsStr = strings.TrimSpace(coinsStr)
|
||||
if len(coinsStr) == 0 {
|
||||
return nil, nil
|
||||
func ParseCoinsNormalized(coinStr string) (Coins, error) {
|
||||
coins, err := ParseDecCoins(coinStr)
|
||||
if err != nil {
|
||||
return Coins{}, err
|
||||
}
|
||||
|
||||
coinStrs := strings.Split(coinsStr, ",")
|
||||
coins := make(Coins, len(coinStrs))
|
||||
for i, coinStr := range coinStrs {
|
||||
coin, err := ParseCoin(coinStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
coins[i] = coin
|
||||
}
|
||||
|
||||
newCoins := sanitizeCoins(coins)
|
||||
if err := newCoins.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newCoins, nil
|
||||
return NormalizeCoins(coins), nil
|
||||
}
|
||||
|
||||
@ -101,9 +101,9 @@ func (s *coinTestSuite) TestCoinIsValid() {
|
||||
func (s *coinTestSuite) TestCustomValidation() {
|
||||
|
||||
newDnmRegex := `[\x{1F600}-\x{1F6FF}]`
|
||||
sdk.CoinDenomRegex = func() string {
|
||||
sdk.SetCoinDenomRegex(func() string {
|
||||
return newDnmRegex
|
||||
}
|
||||
})
|
||||
|
||||
cases := []struct {
|
||||
coin sdk.Coin
|
||||
@ -119,7 +119,7 @@ func (s *coinTestSuite) TestCustomValidation() {
|
||||
for i, tc := range cases {
|
||||
s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i)
|
||||
}
|
||||
sdk.CoinDenomRegex = sdk.DefaultCoinDenomRegex
|
||||
sdk.SetCoinDenomRegex(sdk.DefaultCoinDenomRegex)
|
||||
}
|
||||
|
||||
func (s *coinTestSuite) TestAddCoin() {
|
||||
@ -660,18 +660,18 @@ func (s *coinTestSuite) TestParseCoins() {
|
||||
{"98 bar , 1 foo ", true, sdk.Coins{{"bar", sdk.NewInt(98)}, {"foo", one}}},
|
||||
{" 55\t \t bling\n", true, sdk.Coins{{"bling", sdk.NewInt(55)}}},
|
||||
{"2foo, 97 bar", true, sdk.Coins{{"bar", sdk.NewInt(97)}, {"foo", sdk.NewInt(2)}}},
|
||||
{"5 mycoin,", false, nil}, // no empty coins in a list
|
||||
{"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name
|
||||
{"11me coin, 12you coin", false, nil}, // no spaces in coin names
|
||||
{"1.2btc", false, nil}, // amount must be integer
|
||||
{"5foo:bar", false, nil}, // invalid separator
|
||||
{"5 mycoin,", false, nil}, // no empty coins in a list
|
||||
{"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name
|
||||
{"11me coin, 12you coin", false, nil}, // no spaces in coin names
|
||||
{"1.2btc", true, sdk.Coins{{"btc", sdk.NewInt(1)}}}, // amount can be decimal, will get truncated
|
||||
{"5foo:bar", false, nil}, // invalid separator
|
||||
{"10atom10", true, sdk.Coins{{"atom10", sdk.NewInt(10)}}},
|
||||
{"200transfer/channelToA/uatom", true, sdk.Coins{{"transfer/channelToA/uatom", sdk.NewInt(200)}}},
|
||||
{"50ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", true, sdk.Coins{{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.NewInt(50)}}},
|
||||
}
|
||||
|
||||
for tcIndex, tc := range cases {
|
||||
res, err := sdk.ParseCoins(tc.input)
|
||||
res, err := sdk.ParseCoinsNormalized(tc.input)
|
||||
if !tc.valid {
|
||||
s.Require().Error(err, "%s: %#v. tc #%d", tc.input, res, tcIndex)
|
||||
} else if s.Assert().Nil(err, "%s: %+v", tc.input, err) {
|
||||
|
||||
@ -615,7 +615,7 @@ func (coins DecCoins) Sort() DecCoins {
|
||||
func ParseDecCoin(coinStr string) (coin DecCoin, err error) {
|
||||
coinStr = strings.TrimSpace(coinStr)
|
||||
|
||||
matches := reDecCoin().FindStringSubmatch(coinStr)
|
||||
matches := reDecCoin.FindStringSubmatch(coinStr)
|
||||
if matches == nil {
|
||||
return DecCoin{}, fmt.Errorf("invalid decimal coin expression: %s", coinStr)
|
||||
}
|
||||
|
||||
@ -352,8 +352,11 @@ func (s *decCoinTestSuite) TestParseDecCoins() {
|
||||
expectedErr bool
|
||||
}{
|
||||
{"", nil, false},
|
||||
{"4stake", nil, true},
|
||||
{"5.5atom,4stake", nil, true},
|
||||
{"4stake", sdk.DecCoins{sdk.NewDecCoinFromDec("stake", sdk.NewDecFromInt(sdk.NewInt(4)))}, false},
|
||||
{"5.5atom,4stake", sdk.DecCoins{
|
||||
sdk.NewDecCoinFromDec("atom", sdk.NewDecWithPrec(5500000000000000000, sdk.Precision)),
|
||||
sdk.NewDecCoinFromDec("stake", sdk.NewDec(4)),
|
||||
}, false},
|
||||
{"0.0stake", sdk.DecCoins{}, false}, // remove zero coins
|
||||
{"10.0btc,1.0atom,20.0btc", nil, true},
|
||||
{
|
||||
|
||||
@ -8,6 +8,9 @@ import (
|
||||
// multipliers (e.g. 1atom = 10^-6uatom).
|
||||
var denomUnits = map[string]Dec{}
|
||||
|
||||
// baseDenom is the denom of smallest unit registered
|
||||
var baseDenom string = ""
|
||||
|
||||
// RegisterDenom registers a denomination with a corresponding unit. If the
|
||||
// denomination is already registered, an error will be returned.
|
||||
func RegisterDenom(denom string, unit Dec) error {
|
||||
@ -20,6 +23,10 @@ func RegisterDenom(denom string, unit Dec) error {
|
||||
}
|
||||
|
||||
denomUnits[denom] = unit
|
||||
|
||||
if baseDenom == "" || unit.LT(denomUnits[baseDenom]) {
|
||||
baseDenom = denom
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -38,6 +45,14 @@ func GetDenomUnit(denom string) (Dec, bool) {
|
||||
return unit, true
|
||||
}
|
||||
|
||||
// GetBaseDenom returns the denom of smallest unit registered
|
||||
func GetBaseDenom() (string, error) {
|
||||
if baseDenom == "" {
|
||||
return "", fmt.Errorf("no denom is registered")
|
||||
}
|
||||
return baseDenom, nil
|
||||
}
|
||||
|
||||
// ConvertCoin attempts to convert a coin to a given denomination. If the given
|
||||
// denomination is invalid or if neither denomination is registered, an error
|
||||
// is returned.
|
||||
@ -60,5 +75,73 @@ func ConvertCoin(coin Coin, denom string) (Coin, error) {
|
||||
return NewCoin(denom, coin.Amount), nil
|
||||
}
|
||||
|
||||
return NewCoin(denom, coin.Amount.ToDec().Mul(srcUnit.Quo(dstUnit)).TruncateInt()), nil
|
||||
return NewCoin(denom, coin.Amount.ToDec().Mul(srcUnit).Quo(dstUnit).TruncateInt()), nil
|
||||
}
|
||||
|
||||
// ConvertDecCoin attempts to convert a decimal coin to a given denomination. If the given
|
||||
// denomination is invalid or if neither denomination is registered, an error
|
||||
// is returned.
|
||||
func ConvertDecCoin(coin DecCoin, denom string) (DecCoin, error) {
|
||||
if err := ValidateDenom(denom); err != nil {
|
||||
return DecCoin{}, err
|
||||
}
|
||||
|
||||
srcUnit, ok := GetDenomUnit(coin.Denom)
|
||||
if !ok {
|
||||
return DecCoin{}, fmt.Errorf("source denom not registered: %s", coin.Denom)
|
||||
}
|
||||
|
||||
dstUnit, ok := GetDenomUnit(denom)
|
||||
if !ok {
|
||||
return DecCoin{}, fmt.Errorf("destination denom not registered: %s", denom)
|
||||
}
|
||||
|
||||
if srcUnit.Equal(dstUnit) {
|
||||
return NewDecCoinFromDec(denom, coin.Amount), nil
|
||||
}
|
||||
|
||||
return NewDecCoinFromDec(denom, coin.Amount.Mul(srcUnit).Quo(dstUnit)), nil
|
||||
}
|
||||
|
||||
// NormalizeCoin try to convert a coin to the smallest unit registered,
|
||||
// returns original one if failed.
|
||||
func NormalizeCoin(coin Coin) Coin {
|
||||
base, err := GetBaseDenom()
|
||||
if err != nil {
|
||||
return coin
|
||||
}
|
||||
newCoin, err := ConvertCoin(coin, base)
|
||||
if err != nil {
|
||||
return coin
|
||||
}
|
||||
return newCoin
|
||||
}
|
||||
|
||||
// NormalizeDecCoin try to convert a decimal coin to the smallest unit registered,
|
||||
// returns original one if failed.
|
||||
func NormalizeDecCoin(coin DecCoin) DecCoin {
|
||||
base, err := GetBaseDenom()
|
||||
if err != nil {
|
||||
return coin
|
||||
}
|
||||
newCoin, err := ConvertDecCoin(coin, base)
|
||||
if err != nil {
|
||||
return coin
|
||||
}
|
||||
return newCoin
|
||||
}
|
||||
|
||||
// NormalizeCoins normalize and truncate a list of decimal coins
|
||||
func NormalizeCoins(coins []DecCoin) Coins {
|
||||
if coins == nil {
|
||||
return nil
|
||||
}
|
||||
result := make([]Coin, 0, len(coins))
|
||||
|
||||
for _, coin := range coins {
|
||||
newCoin, _ := NormalizeDecCoin(coin).TruncateDecimal()
|
||||
result = append(result, newCoin)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ func (s *internalDenomTestSuite) TestRegisterDenom() {
|
||||
s.Require().Equal(ZeroDec(), res)
|
||||
|
||||
// reset registration
|
||||
baseDenom = ""
|
||||
denomUnits = map[string]Dec{}
|
||||
}
|
||||
|
||||
@ -52,6 +53,21 @@ func (s *internalDenomTestSuite) TestConvertCoins() {
|
||||
natomUnit := NewDecWithPrec(1, 9) // 10^-9 (nano)
|
||||
s.Require().NoError(RegisterDenom(natom, natomUnit))
|
||||
|
||||
res, err := GetBaseDenom()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res, natom)
|
||||
s.Require().Equal(NormalizeCoin(NewCoin(uatom, NewInt(1))), NewCoin(natom, NewInt(1000)))
|
||||
s.Require().Equal(NormalizeCoin(NewCoin(matom, NewInt(1))), NewCoin(natom, NewInt(1000000)))
|
||||
s.Require().Equal(NormalizeCoin(NewCoin(atom, NewInt(1))), NewCoin(natom, NewInt(1000000000)))
|
||||
|
||||
coins, err := ParseCoinsNormalized("1atom,1matom,1uatom")
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(coins, Coins{
|
||||
Coin{natom, NewInt(1000000000)},
|
||||
Coin{natom, NewInt(1000000)},
|
||||
Coin{natom, NewInt(1000)},
|
||||
})
|
||||
|
||||
testCases := []struct {
|
||||
input Coin
|
||||
denom string
|
||||
@ -87,5 +103,90 @@ func (s *internalDenomTestSuite) TestConvertCoins() {
|
||||
}
|
||||
|
||||
// reset registration
|
||||
baseDenom = ""
|
||||
denomUnits = map[string]Dec{}
|
||||
}
|
||||
|
||||
func (s *internalDenomTestSuite) TestConvertDecCoins() {
|
||||
atomUnit := OneDec() // 1 (base denom unit)
|
||||
s.Require().NoError(RegisterDenom(atom, atomUnit))
|
||||
|
||||
matomUnit := NewDecWithPrec(1, 3) // 10^-3 (milli)
|
||||
s.Require().NoError(RegisterDenom(matom, matomUnit))
|
||||
|
||||
uatomUnit := NewDecWithPrec(1, 6) // 10^-6 (micro)
|
||||
s.Require().NoError(RegisterDenom(uatom, uatomUnit))
|
||||
|
||||
natomUnit := NewDecWithPrec(1, 9) // 10^-9 (nano)
|
||||
s.Require().NoError(RegisterDenom(natom, natomUnit))
|
||||
|
||||
res, err := GetBaseDenom()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res, natom)
|
||||
s.Require().Equal(NormalizeDecCoin(NewDecCoin(uatom, NewInt(1))), NewDecCoin(natom, NewInt(1000)))
|
||||
s.Require().Equal(NormalizeDecCoin(NewDecCoin(matom, NewInt(1))), NewDecCoin(natom, NewInt(1000000)))
|
||||
s.Require().Equal(NormalizeDecCoin(NewDecCoin(atom, NewInt(1))), NewDecCoin(natom, NewInt(1000000000)))
|
||||
|
||||
coins, err := ParseCoinsNormalized("0.1atom,0.1matom,0.1uatom")
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(coins, Coins{
|
||||
Coin{natom, NewInt(100000000)},
|
||||
Coin{natom, NewInt(100000)},
|
||||
Coin{natom, NewInt(100)},
|
||||
})
|
||||
|
||||
testCases := []struct {
|
||||
input DecCoin
|
||||
denom string
|
||||
result DecCoin
|
||||
expErr bool
|
||||
}{
|
||||
{NewDecCoin("foo", ZeroInt()), atom, DecCoin{}, true},
|
||||
{NewDecCoin(atom, ZeroInt()), "foo", DecCoin{}, true},
|
||||
{NewDecCoin(atom, ZeroInt()), "FOO", DecCoin{}, true},
|
||||
|
||||
// 0.5atom
|
||||
{NewDecCoinFromDec(atom, NewDecWithPrec(5, 1)), matom, NewDecCoin(matom, NewInt(500)), false}, // atom => matom
|
||||
{NewDecCoinFromDec(atom, NewDecWithPrec(5, 1)), uatom, NewDecCoin(uatom, NewInt(500000)), false}, // atom => uatom
|
||||
{NewDecCoinFromDec(atom, NewDecWithPrec(5, 1)), natom, NewDecCoin(natom, NewInt(500000000)), false}, // atom => natom
|
||||
|
||||
{NewDecCoin(uatom, NewInt(5000000)), matom, NewDecCoin(matom, NewInt(5000)), false}, // uatom => matom
|
||||
{NewDecCoin(uatom, NewInt(5000000)), natom, NewDecCoin(natom, NewInt(5000000000)), false}, // uatom => natom
|
||||
{NewDecCoin(uatom, NewInt(5000000)), atom, NewDecCoin(atom, NewInt(5)), false}, // uatom => atom
|
||||
|
||||
{NewDecCoin(matom, NewInt(5000)), natom, NewDecCoin(natom, NewInt(5000000000)), false}, // matom => natom
|
||||
{NewDecCoin(matom, NewInt(5000)), uatom, NewDecCoin(uatom, NewInt(5000000)), false}, // matom => uatom
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
res, err := ConvertDecCoin(tc.input, tc.denom)
|
||||
s.Require().Equal(
|
||||
tc.expErr, err != nil,
|
||||
"unexpected error; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom,
|
||||
)
|
||||
s.Require().Equal(
|
||||
tc.result, res,
|
||||
"invalid result; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom,
|
||||
)
|
||||
}
|
||||
|
||||
// reset registration
|
||||
baseDenom = ""
|
||||
denomUnits = map[string]Dec{}
|
||||
}
|
||||
|
||||
func (s *internalDenomTestSuite) TestDecOperationOrder() {
|
||||
dec, err := NewDecFromStr("11")
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(RegisterDenom("unit1", dec))
|
||||
dec, err = NewDecFromStr("100000011")
|
||||
s.Require().NoError(RegisterDenom("unit2", dec))
|
||||
|
||||
coin, err := ConvertCoin(NewCoin("unit1", NewInt(100000011)), "unit2")
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(coin, NewCoin("unit2", NewInt(11)))
|
||||
|
||||
// reset registration
|
||||
baseDenom = ""
|
||||
denomUnits = map[string]Dec{}
|
||||
}
|
||||
|
||||
@ -162,13 +162,13 @@ type AppModule interface {
|
||||
// registers
|
||||
RegisterInvariants(sdk.InvariantRegistry)
|
||||
|
||||
// routes
|
||||
// Deprecated: use RegisterServices
|
||||
Route() sdk.Route
|
||||
|
||||
// Deprecated: use RegisterQueryService
|
||||
// Deprecated: use RegisterServices
|
||||
QuerierRoute() string
|
||||
|
||||
// Deprecated: use RegisterQueryService
|
||||
// Deprecated: use RegisterServices
|
||||
LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier
|
||||
|
||||
// RegisterServices allows a module to register services
|
||||
|
||||
@ -3,6 +3,9 @@ package query
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/types"
|
||||
)
|
||||
|
||||
@ -10,6 +13,30 @@ import (
|
||||
// if the `limit` is not supplied, paginate will use `DefaultLimit`
|
||||
const DefaultLimit = 100
|
||||
|
||||
// ParsePagination validate PageRequest and returns page number & limit.
|
||||
func ParsePagination(pageReq *PageRequest) (page, limit int, err error) {
|
||||
offset := 0
|
||||
limit = DefaultLimit
|
||||
|
||||
if pageReq != nil {
|
||||
offset = int(pageReq.Offset)
|
||||
limit = int(pageReq.Limit)
|
||||
}
|
||||
if offset < 0 {
|
||||
return 1, 0, status.Error(codes.InvalidArgument, "offset must greater than 0")
|
||||
}
|
||||
|
||||
if limit < 0 {
|
||||
return 1, 0, status.Error(codes.InvalidArgument, "limit must greater than 0")
|
||||
} else if limit == 0 {
|
||||
limit = DefaultLimit
|
||||
}
|
||||
|
||||
page = offset/limit + 1
|
||||
|
||||
return page, limit, nil
|
||||
}
|
||||
|
||||
// Paginate does pagination of all the results in the PrefixStore based on the
|
||||
// provided PageRequest. onResult should be used to do actual unmarshaling.
|
||||
func Paginate(
|
||||
|
||||
@ -43,6 +43,25 @@ func TestPaginationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(paginationTestSuite))
|
||||
}
|
||||
|
||||
func (s *paginationTestSuite) TestParsePagination() {
|
||||
s.T().Log("verify default values for empty page request")
|
||||
pageReq := &query.PageRequest{}
|
||||
page, limit, err := query.ParsePagination(pageReq)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(limit, query.DefaultLimit)
|
||||
s.Require().Equal(page, 1)
|
||||
|
||||
s.T().Log("verify with custom values")
|
||||
pageReq = &query.PageRequest{
|
||||
Offset: 0,
|
||||
Limit: 10,
|
||||
}
|
||||
page, limit, err = query.ParsePagination(pageReq)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(page, 1)
|
||||
s.Require().Equal(limit, 10)
|
||||
}
|
||||
|
||||
func (s *paginationTestSuite) TestPagination() {
|
||||
app, ctx, _ := setupTest()
|
||||
queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry())
|
||||
|
||||
3934
types/query/query.pb.go
Normal file
3934
types/query/query.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
566
types/query/query.pb.gw.go
Normal file
566
types/query/query.pb.gw.go
Normal file
@ -0,0 +1,566 @@
|
||||
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
||||
// source: cosmos/base/tendermint/v1beta1/query.proto
|
||||
|
||||
/*
|
||||
Package query is a reverse proxy.
|
||||
|
||||
It translates gRPC into RESTful JSON APIs.
|
||||
*/
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/golang/protobuf/descriptor"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/utilities"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Suppress "imported and not used" errors
|
||||
var _ codes.Code
|
||||
var _ io.Reader
|
||||
var _ status.Status
|
||||
var _ = runtime.String
|
||||
var _ = utilities.NewDoubleArray
|
||||
var _ = descriptor.ForMessage
|
||||
|
||||
func request_Service_GetNodeInfo_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetNodeInfoRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := client.GetNodeInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Service_GetNodeInfo_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetNodeInfoRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := server.GetNodeInfo(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_Service_GetSyncing_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetSyncingRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := client.GetSyncing(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Service_GetSyncing_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetSyncingRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := server.GetSyncing(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_Service_GetLatestBlock_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetLatestBlockRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := client.GetLatestBlock(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Service_GetLatestBlock_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetLatestBlockRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := server.GetLatestBlock(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_Service_GetBlockByHeight_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetBlockByHeightRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["height"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height")
|
||||
}
|
||||
|
||||
protoReq.Height, err = runtime.Int64(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err)
|
||||
}
|
||||
|
||||
msg, err := client.GetBlockByHeight(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Service_GetBlockByHeight_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetBlockByHeightRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["height"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height")
|
||||
}
|
||||
|
||||
protoReq.Height, err = runtime.Int64(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err)
|
||||
}
|
||||
|
||||
msg, err := server.GetBlockByHeight(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
var (
|
||||
filter_Service_GetLatestValidatorSet_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
||||
)
|
||||
|
||||
func request_Service_GetLatestValidatorSet_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetLatestValidatorSetRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Service_GetLatestValidatorSet_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.GetLatestValidatorSet(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Service_GetLatestValidatorSet_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetLatestValidatorSetRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Service_GetLatestValidatorSet_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.GetLatestValidatorSet(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
var (
|
||||
filter_Service_GetValidatorSetByHeight_0 = &utilities.DoubleArray{Encoding: map[string]int{"height": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
|
||||
)
|
||||
|
||||
func request_Service_GetValidatorSetByHeight_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetValidatorSetByHeightRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["height"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height")
|
||||
}
|
||||
|
||||
protoReq.Height, err = runtime.Int64(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err)
|
||||
}
|
||||
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Service_GetValidatorSetByHeight_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.GetValidatorSetByHeight(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Service_GetValidatorSetByHeight_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GetValidatorSetByHeightRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["height"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height")
|
||||
}
|
||||
|
||||
protoReq.Height, err = runtime.Int64(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err)
|
||||
}
|
||||
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Service_GetValidatorSetByHeight_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.GetValidatorSetByHeight(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
// RegisterServiceHandlerServer registers the http handlers for service Service to "mux".
|
||||
// UnaryRPC :call ServiceServer directly.
|
||||
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
||||
// Note that using this registration option will cause many gRPC library features (such as grpc.SendHeader, etc) to stop working. Consider using RegisterServiceHandlerFromEndpoint instead.
|
||||
func RegisterServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ServiceServer) error {
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetNodeInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Service_GetNodeInfo_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetNodeInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetSyncing_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Service_GetSyncing_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetSyncing_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetLatestBlock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Service_GetLatestBlock_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetLatestBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetBlockByHeight_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Service_GetBlockByHeight_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetBlockByHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetLatestValidatorSet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Service_GetLatestValidatorSet_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetLatestValidatorSet_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetValidatorSetByHeight_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Service_GetValidatorSetByHeight_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetValidatorSetByHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterServiceHandlerFromEndpoint is same as RegisterServiceHandler but
|
||||
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
||||
func RegisterServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
||||
conn, err := grpc.Dial(endpoint, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if cerr := conn.Close(); cerr != nil {
|
||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
||||
}
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
if cerr := conn.Close(); cerr != nil {
|
||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
||||
}
|
||||
}()
|
||||
}()
|
||||
|
||||
return RegisterServiceHandler(ctx, mux, conn)
|
||||
}
|
||||
|
||||
// RegisterServiceHandler registers the http handlers for service Service to "mux".
|
||||
// The handlers forward requests to the grpc endpoint over "conn".
|
||||
func RegisterServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
|
||||
return RegisterServiceHandlerClient(ctx, mux, NewServiceClient(conn))
|
||||
}
|
||||
|
||||
// RegisterServiceHandlerClient registers the http handlers for service Service
|
||||
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ServiceClient".
|
||||
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ServiceClient"
|
||||
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
|
||||
// "ServiceClient" to call the correct interceptors.
|
||||
func RegisterServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ServiceClient) error {
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetNodeInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Service_GetNodeInfo_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetNodeInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetSyncing_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Service_GetSyncing_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetSyncing_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetLatestBlock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Service_GetLatestBlock_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetLatestBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetBlockByHeight_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Service_GetBlockByHeight_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetBlockByHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetLatestValidatorSet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Service_GetLatestValidatorSet_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetLatestValidatorSet_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Service_GetValidatorSetByHeight_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Service_GetValidatorSetByHeight_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Service_GetValidatorSetByHeight_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
pattern_Service_GetNodeInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"cosmos", "base", "tendermint", "v1beta1", "node_info"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
||||
pattern_Service_GetSyncing_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"cosmos", "base", "tendermint", "v1beta1", "syncing"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
||||
pattern_Service_GetLatestBlock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "blocks", "latest"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
||||
pattern_Service_GetBlockByHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "blocks", "height"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
||||
pattern_Service_GetLatestValidatorSet_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "validators", "latest"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
||||
pattern_Service_GetValidatorSetByHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"cosmos", "base", "tendermint", "v1beta1", "validators", "height"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
)
|
||||
|
||||
var (
|
||||
forward_Service_GetNodeInfo_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Service_GetSyncing_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Service_GetLatestBlock_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Service_GetBlockByHeight_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Service_GetLatestValidatorSet_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Service_GetValidatorSetByHeight_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
@ -42,7 +42,7 @@ func TestBaseReq_Sanitize(t *testing.T) {
|
||||
|
||||
func TestBaseReq_ValidateBasic(t *testing.T) {
|
||||
fromAddr := "cosmos1cq0sxam6x4l0sv9yz3a2vlqhdhvt2k6jtgcse0"
|
||||
tenstakes, err := types.ParseCoins("10stake")
|
||||
tenstakes, err := types.ParseCoinsNormalized("10stake")
|
||||
require.NoError(t, err)
|
||||
onestake, err := types.ParseDecCoins("1.0stake")
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -56,7 +56,7 @@ func TestRandStringOfLength(t *testing.T) {
|
||||
}
|
||||
|
||||
func mustParseCoins(s string) sdk.Coins {
|
||||
coins, err := sdk.ParseCoins(s)
|
||||
coins, err := sdk.ParseCoinsNormalized(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -85,6 +85,13 @@ type buildDep struct {
|
||||
*debug.Module
|
||||
}
|
||||
|
||||
func (d buildDep) String() string { return fmt.Sprintf("%s@%s", d.Path, d.Version) }
|
||||
func (d buildDep) String() string {
|
||||
if d.Replace != nil {
|
||||
return fmt.Sprintf("%s@%s => %s@%s", d.Path, d.Version, d.Replace.Path, d.Replace.Version)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s@%s", d.Path, d.Version)
|
||||
}
|
||||
|
||||
func (d buildDep) MarshalJSON() ([]byte, error) { return json.Marshal(d.String()) }
|
||||
func (d buildDep) MarshalYAML() (interface{}, error) { return d.String(), nil }
|
||||
|
||||
@ -55,6 +55,13 @@ func DecodeTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
|
||||
|
||||
response := DecodeResp(stdTx)
|
||||
|
||||
err = checkSignModeError(clientCtx, response, "/cosmos/tx/v1beta1/txs/decode")
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
rest.PostProcessResponse(w, clientCtx, response)
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
clientrest "github.com/cosmos/cosmos-sdk/client/rest"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
@ -17,7 +18,7 @@ import (
|
||||
genutilrest "github.com/cosmos/cosmos-sdk/x/genutil/client/rest"
|
||||
)
|
||||
|
||||
// query accountREST Handler
|
||||
// QueryAccountRequestHandlerFn is the query accountREST Handler.
|
||||
func QueryAccountRequestHandlerFn(storeName string, clientCtx client.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
@ -107,6 +108,13 @@ func QueryTxsRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
|
||||
packStdTxResponse(w, clientCtx, txRes)
|
||||
}
|
||||
|
||||
err = checkSignModeError(clientCtx, searchResult, "/cosmos/tx/v1beta1/txs")
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
rest.PostProcessResponseBare(w, clientCtx, searchResult)
|
||||
}
|
||||
}
|
||||
@ -143,6 +151,13 @@ func QueryTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
|
||||
rest.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("no transaction found with hash %s", hashHexStr))
|
||||
}
|
||||
|
||||
err = checkSignModeError(clientCtx, output, "/cosmos/tx/v1beta1/tx/{txhash}")
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
rest.PostProcessResponseBare(w, clientCtx, output)
|
||||
}
|
||||
}
|
||||
@ -182,3 +197,24 @@ func packStdTxResponse(w http.ResponseWriter, clientCtx client.Context, txRes *s
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkSignModeError checks if there are errors with marshalling non-amino
|
||||
// txs with amino.
|
||||
func checkSignModeError(ctx client.Context, resp interface{}, grpcEndPoint string) error {
|
||||
// LegacyAmino used intentionally here to handle the SignMode errors
|
||||
marshaler := ctx.LegacyAmino
|
||||
|
||||
_, err := marshaler.MarshalJSON(resp)
|
||||
if err != nil {
|
||||
|
||||
// If there's an unmarshalling error, we assume that it's because we're
|
||||
// using amino to unmarshal a non-amino tx.
|
||||
return fmt.Errorf("this transaction cannot be displayed via legacy REST endpoints, because it does not support"+
|
||||
" Amino serialization. Please either use CLI, gRPC, gRPC-gateway, or directly query the Tendermint RPC"+
|
||||
" endpoint to query this transaction. The new REST endpoint (via gRPC-gateway) is %s. Please also see the"+
|
||||
"REST endpoints migration guide at %s for more info", grpcEndPoint, clientrest.DeprecationURL)
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -4,23 +4,29 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
|
||||
bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil"
|
||||
|
||||
authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
||||
rest2 "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
|
||||
bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
ibccli "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/client/cli"
|
||||
ibcsolomachinecli "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/06-solomachine/client/cli"
|
||||
)
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
@ -124,51 +130,99 @@ func (s *IntegrationTestSuite) TestBroadcastTxRequest() {
|
||||
s.Require().NotEmpty(txRes.TxHash)
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestQueryTxByHash() {
|
||||
// Helper function to test querying txs. We will use it to query StdTx and service `Msg`s.
|
||||
func (s *IntegrationTestSuite) testQueryTx(txHeight int64, txHash, txRecipient string) {
|
||||
val0 := s.network.Validators[0]
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
malleate func() *sdk.TxResponse
|
||||
}{
|
||||
{
|
||||
"Query by hash",
|
||||
func() *sdk.TxResponse {
|
||||
txJSON, err := rest.GetRequest(fmt.Sprintf("%s/txs/%s", val0.APIAddress, txHash))
|
||||
s.Require().NoError(err)
|
||||
|
||||
var txResAmino sdk.TxResponse
|
||||
s.Require().NoError(val0.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &txResAmino))
|
||||
return &txResAmino
|
||||
},
|
||||
},
|
||||
{
|
||||
"Query by height",
|
||||
func() *sdk.TxResponse {
|
||||
txJSON, err := rest.GetRequest(fmt.Sprintf("%s/txs?limit=10&page=1&tx.height=%d", val0.APIAddress, txHeight))
|
||||
s.Require().NoError(err)
|
||||
|
||||
var searchtxResult sdk.SearchTxsResult
|
||||
s.Require().NoError(val0.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &searchtxResult))
|
||||
s.Require().Len(searchtxResult.Txs, 1)
|
||||
return searchtxResult.Txs[0]
|
||||
},
|
||||
},
|
||||
{
|
||||
"Query by event (transfer.recipient)",
|
||||
func() *sdk.TxResponse {
|
||||
txJSON, err := rest.GetRequest(fmt.Sprintf("%s/txs?transfer.recipient=%s", val0.APIAddress, txRecipient))
|
||||
s.Require().NoError(err)
|
||||
|
||||
var searchtxResult sdk.SearchTxsResult
|
||||
s.Require().NoError(val0.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &searchtxResult))
|
||||
s.Require().Len(searchtxResult.Txs, 1)
|
||||
return searchtxResult.Txs[0]
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(fmt.Sprintf("Case %s", tc.desc), func() {
|
||||
txResponse := tc.malleate()
|
||||
|
||||
// Check that the height is correct.
|
||||
s.Require().Equal(txHeight, txResponse.Height)
|
||||
|
||||
// Check that the events are correct.
|
||||
s.Require().Contains(
|
||||
txResponse.RawLog,
|
||||
fmt.Sprintf("{\"key\":\"recipient\",\"value\":\"%s\"}", txRecipient),
|
||||
)
|
||||
|
||||
// Check that the Msg is correct.
|
||||
stdTx, ok := txResponse.Tx.GetCachedValue().(legacytx.StdTx)
|
||||
s.Require().True(ok)
|
||||
msgs := stdTx.GetMsgs()
|
||||
s.Require().Equal(len(msgs), 1)
|
||||
msg, ok := msgs[0].(*types.MsgSend)
|
||||
s.Require().True(ok)
|
||||
s.Require().Equal(txRecipient, msg.ToAddress)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestQueryTxWithStdTx() {
|
||||
val0 := s.network.Validators[0]
|
||||
|
||||
// We broadcasted a StdTx in SetupSuite.
|
||||
// we just check for a non-empty TxHash here, the actual hash will depend on the underlying tx configuration
|
||||
// We just check for a non-empty TxHash here, the actual hash will depend on the underlying tx configuration
|
||||
s.Require().NotEmpty(s.stdTxRes.TxHash)
|
||||
|
||||
// We now fetch the tx by hash on the `/tx/{hash}` route.
|
||||
txJSON, err := rest.GetRequest(fmt.Sprintf("%s/txs/%s", val0.APIAddress, s.stdTxRes.TxHash))
|
||||
s.Require().NoError(err)
|
||||
|
||||
// txJSON should contain the whole tx, we just make sure that our custom
|
||||
// memo is there.
|
||||
s.Require().Contains(string(txJSON), s.stdTx.Memo)
|
||||
s.testQueryTx(s.stdTxRes.Height, s.stdTxRes.TxHash, val0.Address.String())
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestQueryTxByHeight() {
|
||||
val0 := s.network.Validators[0]
|
||||
|
||||
// We broadcasted a StdTx in SetupSuite.
|
||||
// we just check for a non-empty height here, as we'll need to for querying.
|
||||
s.Require().NotEmpty(s.stdTxRes.Height)
|
||||
|
||||
// We now fetch the tx on `/txs` route, filtering by `tx.height`
|
||||
txJSON, err := rest.GetRequest(fmt.Sprintf("%s/txs?limit=10&page=1&tx.height=%d", val0.APIAddress, s.stdTxRes.Height))
|
||||
s.Require().NoError(err)
|
||||
|
||||
// txJSON should contain the whole tx, we just make sure that our custom
|
||||
// memo is there.
|
||||
s.Require().Contains(string(txJSON), s.stdTx.Memo)
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestQueryTxByHashWithServiceMessage() {
|
||||
func (s *IntegrationTestSuite) TestQueryTxWithServiceMessage() {
|
||||
val := s.network.Validators[0]
|
||||
|
||||
sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10)
|
||||
_, _, addr := testdata.KeyTestPubAddr()
|
||||
|
||||
// Right after this line, we're sending a tx. Might need to wait a block
|
||||
// to refresh sequences.
|
||||
// Might need to wait a block to refresh sequences from previous setups.
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
|
||||
out, err := bankcli.ServiceMsgSendExec(
|
||||
val.ClientCtx,
|
||||
val.Address,
|
||||
val.Address,
|
||||
addr,
|
||||
sdk.NewCoins(sendTokens),
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
|
||||
@ -183,17 +237,7 @@ func (s *IntegrationTestSuite) TestQueryTxByHashWithServiceMessage() {
|
||||
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
|
||||
txJSON, err := rest.GetRequest(fmt.Sprintf("%s/txs/%s", val.APIAddress, txRes.TxHash))
|
||||
s.Require().NoError(err)
|
||||
|
||||
var txResAmino sdk.TxResponse
|
||||
s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &txResAmino))
|
||||
stdTx, ok := txResAmino.Tx.GetCachedValue().(legacytx.StdTx)
|
||||
s.Require().True(ok)
|
||||
msgs := stdTx.GetMsgs()
|
||||
s.Require().Equal(len(msgs), 1)
|
||||
_, ok = msgs[0].(*types.MsgSend)
|
||||
s.Require().True(ok)
|
||||
s.testQueryTx(txRes.Height, txRes.TxHash, addr.String())
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestMultipleSyncBroadcastTxRequests() {
|
||||
@ -298,6 +342,146 @@ func (s *IntegrationTestSuite) broadcastReq(stdTx legacytx.StdTx, mode string) (
|
||||
return rest.PostRequest(fmt.Sprintf("%s/txs", val.APIAddress), "application/json", bz)
|
||||
}
|
||||
|
||||
// testQueryIBCTx is a helper function to test querying txs which:
|
||||
// - show an error message on legacy REST endpoints
|
||||
// - succeed using gRPC
|
||||
// In practise, we call this function on IBC txs.
|
||||
func (s *IntegrationTestSuite) testQueryIBCTx(txRes sdk.TxResponse, cmd *cobra.Command, args []string) {
|
||||
val := s.network.Validators[0]
|
||||
|
||||
errMsg := "this transaction cannot be displayed via legacy REST endpoints, because it does not support" +
|
||||
" Amino serialization. Please either use CLI, gRPC, gRPC-gateway, or directly query the Tendermint RPC" +
|
||||
" endpoint to query this transaction. The new REST endpoint (via gRPC-gateway) is "
|
||||
|
||||
// Test that legacy endpoint return the above error message on IBC txs.
|
||||
testCases := []struct {
|
||||
desc string
|
||||
url string
|
||||
}{
|
||||
{
|
||||
"Query by hash",
|
||||
fmt.Sprintf("%s/txs/%s", val.APIAddress, txRes.TxHash),
|
||||
},
|
||||
{
|
||||
"Query by height",
|
||||
fmt.Sprintf("%s/txs?tx.height=%d", val.APIAddress, txRes.Height),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(fmt.Sprintf("Case %s", tc.desc), func() {
|
||||
txJSON, err := rest.GetRequest(tc.url)
|
||||
s.Require().NoError(err)
|
||||
|
||||
var errResp rest.ErrorResponse
|
||||
s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(txJSON, &errResp))
|
||||
|
||||
s.Require().Contains(errResp.Error, errMsg)
|
||||
})
|
||||
}
|
||||
|
||||
// try fetching the txn using gRPC req, it will fetch info since it has proto codec.
|
||||
grpcJSON, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/tx/%s", val.APIAddress, txRes.TxHash))
|
||||
s.Require().NoError(err)
|
||||
|
||||
var getTxRes txtypes.GetTxResponse
|
||||
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(grpcJSON, &getTxRes))
|
||||
s.Require().Equal(getTxRes.Tx.Body.Memo, "foobar")
|
||||
|
||||
// generate broadcast only txn.
|
||||
args = append(args, fmt.Sprintf("--%s=true", flags.FlagGenerateOnly))
|
||||
out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args)
|
||||
s.Require().NoError(err)
|
||||
|
||||
txFile, cleanup := testutil.WriteToNewTempFile(s.T(), string(out.Bytes()))
|
||||
txFileName := txFile.Name()
|
||||
s.T().Cleanup(cleanup)
|
||||
|
||||
// encode the generated txn.
|
||||
out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, authcli.GetEncodeCommand(), []string{txFileName})
|
||||
s.Require().NoError(err)
|
||||
|
||||
bz, err := val.ClientCtx.LegacyAmino.MarshalJSON(rest2.DecodeReq{Tx: string(out.Bytes())})
|
||||
s.Require().NoError(err)
|
||||
|
||||
// try to decode the txn using legacy rest, it fails.
|
||||
res, err := rest.PostRequest(fmt.Sprintf("%s/txs/decode", val.APIAddress), "application/json", bz)
|
||||
s.Require().NoError(err)
|
||||
|
||||
var errResp rest.ErrorResponse
|
||||
s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(res, &errResp))
|
||||
s.Require().Contains(errResp.Error, errMsg)
|
||||
}
|
||||
|
||||
// TestLegacyRestErrMessages creates two IBC txs, one that fails, one that
|
||||
// succeeds, and make sure we cannot query any of them (with pretty error msg).
|
||||
// Our intension is to test the error message of querying a message which is
|
||||
// signed with proto, since IBC won't support legacy amino at all we are
|
||||
// considering a message from IBC module.
|
||||
func (s *IntegrationTestSuite) TestLegacyRestErrMessages() {
|
||||
val := s.network.Validators[0]
|
||||
|
||||
// Write consensus json to temp file, used for an IBC message.
|
||||
consensusJSON, cleanup := testutil.WriteToNewTempFile(
|
||||
s.T(),
|
||||
`{"public_key":{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A/3SXL2ONYaOkxpdR5P8tHTlSlPv1AwQwSFxKRee5JQW"},"diversifier":"diversifier","timestamp":"10"}`,
|
||||
)
|
||||
defer cleanup()
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
cmd *cobra.Command
|
||||
args []string
|
||||
code uint32
|
||||
}{
|
||||
{
|
||||
"Failing IBC message",
|
||||
ibccli.NewChannelCloseInitCmd(),
|
||||
[]string{
|
||||
"121", // dummy port-id
|
||||
"channel-0", // dummy channel-id
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
|
||||
fmt.Sprintf("--gas=%d", flags.DefaultGasLimit),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
|
||||
fmt.Sprintf("--%s=foobar", flags.FlagMemo),
|
||||
},
|
||||
uint32(7),
|
||||
},
|
||||
{
|
||||
"Successful IBC message",
|
||||
ibcsolomachinecli.NewCreateClientCmd(),
|
||||
[]string{
|
||||
"21212121212", // dummy client-id
|
||||
"1", // dummy sequence
|
||||
consensusJSON.Name(), // path to consensus json,
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
|
||||
fmt.Sprintf("--gas=%d", flags.DefaultGasLimit),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
|
||||
fmt.Sprintf("--%s=foobar", flags.FlagMemo),
|
||||
},
|
||||
uint32(0),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.Run(fmt.Sprintf("Case %s", tc.desc), func() {
|
||||
out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, tc.cmd, tc.args)
|
||||
s.Require().NoError(err)
|
||||
var txRes sdk.TxResponse
|
||||
s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txRes))
|
||||
s.Require().Equal(tc.code, txRes.Code)
|
||||
|
||||
s.Require().NoError(s.network.WaitForNextBlock())
|
||||
|
||||
s.testQueryIBCTx(txRes, tc.cmd, tc.args)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd
|
||||
return keeper.NewQuerier(am.accountKeeper, legacyQuerierCdc)
|
||||
}
|
||||
|
||||
// RegisterQueryService registers a GRPC query service to respond to the
|
||||
// RegisterServices registers a GRPC query service to respond to the
|
||||
// module-specific GRPC queries.
|
||||
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
||||
types.RegisterQueryServer(cfg.QueryServer(), am.accountKeeper)
|
||||
|
||||
@ -58,7 +58,7 @@ timestamp.`,
|
||||
return err
|
||||
}
|
||||
|
||||
amount, err := sdk.ParseCoins(args[1])
|
||||
amount, err := sdk.ParseCoinsNormalized(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
208
x/bank/atlas/atlas-v0.39.1.md
Normal file
208
x/bank/atlas/atlas-v0.39.1.md
Normal file
@ -0,0 +1,208 @@
|
||||
# x/bank
|
||||
|
||||
The `x/bank` module is responsible for handling multi-asset coin transfers between
|
||||
accounts and tracking special-case pseudo-transfers which must work differently
|
||||
with particular kinds of accounts.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Import the module.
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
```
|
||||
|
||||
2. Add `AppModuleBasic` to your `ModuleBasics`.
|
||||
|
||||
```go
|
||||
var (
|
||||
ModuleBasics = module.NewBasicManager(
|
||||
// ...
|
||||
bank.AppModuleBasic{},
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
3. Create the module's parameter subspace in your application constructor.
|
||||
|
||||
```go
|
||||
func NewApp(...) *App {
|
||||
// ...
|
||||
app.subspaces[bank.ModuleName] = app.ParamsKeeper.Subspace(bank.DefaultParamspace)
|
||||
}
|
||||
```
|
||||
|
||||
4. Create the keeper. Note, the `x/bank` module depends on the `x/auth` module
|
||||
and a list of blacklisted account addresses which funds are not allowed to be
|
||||
sent to. Your application will need to define this method based your needs.
|
||||
|
||||
```go
|
||||
func NewApp(...) *App {
|
||||
// ...
|
||||
app.BankKeeper = bank.NewBaseKeeper(
|
||||
app.AccountKeeper, app.subspaces[bank.ModuleName], app.BlacklistedAccAddrs(),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
5. Add the `x/bank` module to the app's `ModuleManager`.
|
||||
|
||||
```go
|
||||
func NewApp(...) *App {
|
||||
// ...
|
||||
app.mm = module.NewManager(
|
||||
// ...
|
||||
bank.NewAppModule(app.BankKeeper, app.AccountKeeper),
|
||||
// ...
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
6. Set the `x/bank` module genesis order.
|
||||
|
||||
```go
|
||||
func NewApp(...) *App {
|
||||
// ...
|
||||
app.mm.SetOrderInitGenesis(..., bank.ModuleName, ...)
|
||||
}
|
||||
```
|
||||
|
||||
7. Add the `x/bank` module to the simulation manager (if you have one set).
|
||||
|
||||
```go
|
||||
func NewApp(...) *App {
|
||||
// ...
|
||||
app.sm = module.NewSimulationManager(
|
||||
// ...
|
||||
bank.NewAppModule(app.BankKeeper, app.AccountKeeper),
|
||||
// ...
|
||||
)
|
||||
}
|
||||
|
||||
## Genesis
|
||||
|
||||
The `x/bank` module defines its genesis state as follows:
|
||||
|
||||
```go
|
||||
type GenesisState struct {
|
||||
SendEnabled bool `json:"send_enabled" yaml:"send_enabled"`
|
||||
}
|
||||
```
|
||||
|
||||
The `SendEnabled` parameter determines if transfers are enabled or disabled
|
||||
entirely on the chain. This can be used to start a network without enabling
|
||||
transfers while ensuring critical network functionality is operating as expected.
|
||||
|
||||
## Messages
|
||||
|
||||
### `MsgSend`
|
||||
|
||||
The `x/bank` module allows for transfer of funds from a source account to a
|
||||
destination account.
|
||||
|
||||
```go
|
||||
type MsgSend struct {
|
||||
FromAddress sdk.AccAddress `json:"from_address" yaml:"from_address"`
|
||||
ToAddress sdk.AccAddress `json:"to_address" yaml:"to_address"`
|
||||
Amount sdk.Coins `json:"amount" yaml:"amount"`
|
||||
}
|
||||
```
|
||||
|
||||
### `MsgMultiSend`
|
||||
|
||||
The `x/bank` module also allows for multiple inputs and outputs. The sum of all
|
||||
inputs must be equivalent to the sum of all outputs.
|
||||
|
||||
```go
|
||||
type Input struct {
|
||||
Address sdk.AccAddress `json:"address" yaml:"address"`
|
||||
Coins sdk.Coins `json:"coins" yaml:"coins"`
|
||||
}
|
||||
|
||||
type Output struct {
|
||||
Address sdk.AccAddress `json:"address" yaml:"address"`
|
||||
Coins sdk.Coins `json:"coins" yaml:"coins"`
|
||||
}
|
||||
|
||||
type MsgMultiSend struct {
|
||||
Inputs []Input `json:"inputs" yaml:"inputs"`
|
||||
Outputs []Output `json:"outputs" yaml:"outputs"`
|
||||
}
|
||||
```
|
||||
|
||||
## Client
|
||||
|
||||
### CLI
|
||||
|
||||
The `x/bank` supports the following transactional commands.
|
||||
|
||||
1. Send tokens via a `MsgSend` message.
|
||||
|
||||
```shell
|
||||
$ <app> tx send [from_key_or_address] [to_address] [amount] [...flags]
|
||||
```
|
||||
|
||||
Note, the `x/bank` module does not natively support constructing a `MsgMultiSend`
|
||||
message. This type of message must be constructed manually, but it may be signed
|
||||
and broadcasted via the CLI.
|
||||
|
||||
### REST
|
||||
|
||||
The `x/bank` supports various query API endpoints and a `MsgSend` construction
|
||||
endpoint.
|
||||
|
||||
1. Construct an unsigned `MsgSend` transaction.
|
||||
|
||||
| Method | Path |
|
||||
| :----- | :----------------------- |
|
||||
| `POST` | `/bank/accounts/{address}/transfers` |
|
||||
|
||||
Sample payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"base_req": {
|
||||
"chain_id": "chain-foo",
|
||||
"from": "cosmos1u3fneykx9carelvurc6av22vpjvptytj9wklk0",
|
||||
"memo": "memo",
|
||||
"fees": [
|
||||
{
|
||||
"denom": "stake",
|
||||
"amount": "25000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"amount": [
|
||||
{
|
||||
"denom": "stake",
|
||||
"amount": "400000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
2. Query for an account's balance.
|
||||
|
||||
| Method | Path |
|
||||
| :----- | :----------------------- |
|
||||
| `GET` | `/bank/balances/{address}` |
|
||||
|
||||
Sample response:
|
||||
|
||||
```json
|
||||
{
|
||||
"height": "0",
|
||||
"result": [
|
||||
{
|
||||
"denom": "node0token",
|
||||
"amount": "1000000000"
|
||||
},
|
||||
{
|
||||
"denom": "stake",
|
||||
"amount": "400000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user