Merge cosmos-sdk master
This commit is contained in:
parent
2ce6d4bcba
commit
cc4ef50d2a
|
@ -42,6 +42,7 @@ jobs:
|
|||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
|
|
|
@ -31,6 +31,7 @@ jobs:
|
|||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUBTM_USERNAME }}
|
||||
|
|
|
@ -31,6 +31,26 @@ jobs:
|
|||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-tparse-binary
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-arch: ["amd64", "arm", "arm64"]
|
||||
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:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Build
|
||||
run: GOARCH=${{ matrix.go-arch }} LEDGER_ENABLED=false make build
|
||||
|
||||
test-cosmovisor:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -36,20 +36,32 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Client Breaking Changes
|
||||
|
||||
* [\#8363](https://github.com/cosmos/cosmos-sdk/issues/8363) Addresses no longer have a fixed 20-byte length. From the SDK modules' point of view, any 1-255 bytes-long byte array is a valid address.
|
||||
|
||||
### State Machine Breaking
|
||||
|
||||
* (x/{bank,distrib,gov,slashing,staking}) [\#8363](https://github.com/cosmos/cosmos-sdk/issues/8363) Store keys have been modified to allow for variable-length addresses.
|
||||
* (x/ibc) [\#8266](https://github.com/cosmos/cosmos-sdk/issues/8266) Add amino JSON for IBC messages in order to support Ledger text signing.
|
||||
|
||||
### Improvements
|
||||
|
||||
* (x/ibc) [\#8458](https://github.com/cosmos/cosmos-sdk/pull/8458) Add `packet_connection` attribute to ibc events to enable relayer filtering
|
||||
* (x/bank) [\#8479](https://github.com/cosmos/cosmos-sdk/pull/8479) Adittional client denom metadata validation for `base` and `display` denoms.
|
||||
* (x/ibc) [\#8404](https://github.com/cosmos/cosmos-sdk/pull/8404) Reorder IBC `ChanOpenAck` and `ChanOpenConfirm` handler execution to perform core handler first, followed by application callbacks.
|
||||
* [\#8396](https://github.com/cosmos/cosmos-sdk/pull/8396) Add support for ARM platform
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* (x/evidence) [#8461](https://github.com/cosmos/cosmos-sdk/pull/8461) Fix bech32 prefix in evidence validator address conversion
|
||||
* (x/slashing) [\#8427](https://github.com/cosmos/cosmos-sdk/pull/8427) Fix query signing infos command
|
||||
* (simapp) [\#8418](https://github.com/cosmos/cosmos-sdk/pull/8418) Add balance coin to supply when adding a new genesis account
|
||||
* (x/bank) [\#8417](https://github.com/cosmos/cosmos-sdk/pull/8417) Validate balances and coin denom metadata on genesis
|
||||
* (server) [\#8399](https://github.com/cosmos/cosmos-sdk/pull/8399) fix gRPC-web flag default value
|
||||
* (client/keys) [\#8436](https://github.com/cosmos/cosmos-sdk/pull/8436) Fix key migration issue
|
||||
* (server) [\#8481](https://github.com/cosmos/cosmos-sdk/pull/8481) Don't create
|
||||
files when running `{appd} tendermint show-*` subcommands
|
||||
|
||||
## [v0.40.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.1) - 2021-01-19
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -479,4 +479,4 @@ rosetta-data:
|
|||
docker run --name data_dir_build -t rosetta-ci:latest sh /rosetta/data.sh
|
||||
docker cp data_dir_build:/tmp/data.tar.gz "$(CURDIR)/contrib/rosetta/node/data.tar.gz"
|
||||
docker container rm data_dir_build
|
||||
.PHONY: rosetta-data
|
||||
.PHONY: rosetta-data
|
||||
|
|
|
@ -221,6 +221,19 @@ func readTxCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, err
|
|||
clientCtx = clientCtx.WithSignModeStr(signModeStr)
|
||||
}
|
||||
|
||||
if clientCtx.FeeGranter == nil || flagSet.Changed(flags.FlagFeeAccount) {
|
||||
granter, _ := flagSet.GetString(flags.FlagFeeAccount)
|
||||
|
||||
if granter != "" {
|
||||
granterAcc, err := sdk.AccAddressFromBech32(granter)
|
||||
if err != nil {
|
||||
return clientCtx, err
|
||||
}
|
||||
|
||||
clientCtx = clientCtx.WithFeeGranterAddress(granterAcc)
|
||||
}
|
||||
}
|
||||
|
||||
if clientCtx.From == "" || flagSet.Changed(flags.FlagFrom) {
|
||||
from, _ := flagSet.GetString(flags.FlagFrom)
|
||||
fromAddr, fromName, keyType, err := GetFromFields(clientCtx.Keyring, from, clientCtx.GenerateOnly)
|
||||
|
|
|
@ -97,8 +97,7 @@ func TestSetCmdClientContextHandler(t *testing.T) {
|
|||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{})
|
||||
ctx := context.WithValue(context.Background(), client.ClientContextKey, &client.Context{})
|
||||
|
||||
cmd := newCmd()
|
||||
_ = testutil.ApplyMockIODiscardOutErr(cmd)
|
||||
|
|
|
@ -44,6 +44,7 @@ type Context struct {
|
|||
TxConfig TxConfig
|
||||
AccountRetriever AccountRetriever
|
||||
NodeURI string
|
||||
FeeGranter sdk.AccAddress
|
||||
|
||||
// TODO: Deprecated (remove).
|
||||
LegacyAmino *codec.LegacyAmino
|
||||
|
@ -166,6 +167,13 @@ func (ctx Context) WithFromAddress(addr sdk.AccAddress) Context {
|
|||
return ctx
|
||||
}
|
||||
|
||||
// WithFeeGranterAddress returns a copy of the context with an updated fee granter account
|
||||
// address.
|
||||
func (ctx Context) WithFeeGranterAddress(addr sdk.AccAddress) Context {
|
||||
ctx.FeeGranter = addr
|
||||
return ctx
|
||||
}
|
||||
|
||||
// WithBroadcastMode returns a copy of the context with an updated broadcast
|
||||
// mode.
|
||||
func (ctx Context) WithBroadcastMode(mode string) Context {
|
||||
|
|
|
@ -70,6 +70,7 @@ const (
|
|||
FlagCountTotal = "count-total"
|
||||
FlagTimeoutHeight = "timeout-height"
|
||||
FlagKeyAlgorithm = "algo"
|
||||
FlagFeeAccount = "fee-account"
|
||||
|
||||
// Tendermint logging flags
|
||||
FlagLogLevel = "log_level"
|
||||
|
@ -112,6 +113,7 @@ func AddTxFlagsToCmd(cmd *cobra.Command) {
|
|||
cmd.Flags().String(FlagKeyringBackend, DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)")
|
||||
cmd.Flags().String(FlagSignMode, "", "Choose sign mode (direct|amino-json), this is an advanced feature")
|
||||
cmd.Flags().Uint64(FlagTimeoutHeight, 0, "Set a block timeout height to prevent the tx from being committed past a certain height")
|
||||
cmd.Flags().String(FlagFeeAccount, "", "Fee account pays fees for the transaction instead of deducting from the signer")
|
||||
|
||||
// --gas can accept integers and "auto"
|
||||
cmd.Flags().String(FlagGas, "", fmt.Sprintf("gas limit to set per-transaction; set to %q to calculate sufficient gas automatically (default %d)", GasFlagAuto, DefaultGasLimit))
|
||||
|
|
|
@ -8,12 +8,12 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client"
|
||||
)
|
||||
|
||||
func getBlock(clientCtx client.Context, height *int64) (*ctypes.ResultBlock, error) {
|
||||
func getBlock(ctx context.Context, 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)
|
||||
return node.Block(ctx, height)
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ func NewQueryServer(clientCtx client.Context, interfaceRegistry codectypes.Inter
|
|||
}
|
||||
|
||||
// GetSyncing implements ServiceServer.GetSyncing
|
||||
func (s queryServer) GetSyncing(_ context.Context, _ *GetSyncingRequest) (*GetSyncingResponse, error) {
|
||||
status, err := getNodeStatus(s.clientCtx)
|
||||
func (s queryServer) GetSyncing(ctx context.Context, _ *GetSyncingRequest) (*GetSyncingResponse, error) {
|
||||
status, err := getNodeStatus(ctx, s.clientCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -45,8 +45,8 @@ func (s queryServer) GetSyncing(_ context.Context, _ *GetSyncingRequest) (*GetSy
|
|||
}
|
||||
|
||||
// GetLatestBlock implements ServiceServer.GetLatestBlock
|
||||
func (s queryServer) GetLatestBlock(context.Context, *GetLatestBlockRequest) (*GetLatestBlockResponse, error) {
|
||||
status, err := getBlock(s.clientCtx, nil)
|
||||
func (s queryServer) GetLatestBlock(ctx context.Context, _ *GetLatestBlockRequest) (*GetLatestBlockResponse, error) {
|
||||
status, err := getBlock(ctx, s.clientCtx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ func (s queryServer) GetLatestBlock(context.Context, *GetLatestBlockRequest) (*G
|
|||
}
|
||||
|
||||
// GetBlockByHeight implements ServiceServer.GetBlockByHeight
|
||||
func (s queryServer) GetBlockByHeight(_ context.Context, req *GetBlockByHeightRequest) (*GetBlockByHeightResponse, error) {
|
||||
func (s queryServer) GetBlockByHeight(ctx context.Context, req *GetBlockByHeightRequest) (*GetBlockByHeightResponse, error) {
|
||||
chainHeight, err := rpc.GetChainHeight(s.clientCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -74,7 +74,7 @@ func (s queryServer) GetBlockByHeight(_ context.Context, req *GetBlockByHeightRe
|
|||
return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length")
|
||||
}
|
||||
|
||||
res, err := getBlock(s.clientCtx, &req.Height)
|
||||
res, err := getBlock(ctx, s.clientCtx, &req.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ func (s queryServer) GetLatestValidatorSet(ctx context.Context, req *GetLatestVa
|
|||
return nil, err
|
||||
}
|
||||
|
||||
validatorsRes, err := rpc.GetValidators(s.clientCtx, nil, &page, &limit)
|
||||
validatorsRes, err := rpc.GetValidators(ctx, s.clientCtx, nil, &page, &limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida
|
|||
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)
|
||||
validatorsRes, err := rpc.GetValidators(ctx, s.clientCtx, &req.Height, &page, &limit)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -175,7 +175,7 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida
|
|||
|
||||
// GetNodeInfo implements ServiceServer.GetNodeInfo
|
||||
func (s queryServer) GetNodeInfo(ctx context.Context, req *GetNodeInfoRequest) (*GetNodeInfoResponse, error) {
|
||||
status, err := getNodeStatus(s.clientCtx)
|
||||
status, err := getNodeStatus(ctx, s.clientCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/client"
|
||||
)
|
||||
|
||||
func getNodeStatus(clientCtx client.Context) (*ctypes.ResultStatus, error) {
|
||||
func getNodeStatus(ctx context.Context, clientCtx client.Context) (*ctypes.ResultStatus, error) {
|
||||
node, err := clientCtx.GetNode()
|
||||
if err != nil {
|
||||
return &ctypes.ResultStatus{}, err
|
||||
}
|
||||
return node.Status(context.Background())
|
||||
return node.Status(ctx)
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
|||
|
||||
var (
|
||||
tmpDir string
|
||||
migrator keyring.InfoImporter
|
||||
migrator keyring.Importer
|
||||
)
|
||||
|
||||
if dryRun, _ := cmd.Flags().GetBool(flags.FlagDryRun); dryRun {
|
||||
|
@ -73,10 +73,10 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
|||
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
migrator, err = keyring.NewInfoImporter(keyringServiceName, "test", tmpDir, buf)
|
||||
migrator, err = keyring.New(keyringServiceName, keyring.BackendTest, tmpDir, buf)
|
||||
} else {
|
||||
backend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend)
|
||||
migrator, err = keyring.NewInfoImporter(keyringServiceName, backend, rootDir, buf)
|
||||
migrator, err = keyring.New(keyringServiceName, backend, rootDir, buf)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -86,12 +86,12 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
|||
))
|
||||
}
|
||||
|
||||
for _, key := range oldKeys {
|
||||
legKeyInfo, err := legacyKb.Export(key.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(oldKeys) == 0 {
|
||||
cmd.Print("Migration Aborted: no keys to migrate")
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, key := range oldKeys {
|
||||
keyName := key.GetName()
|
||||
keyType := key.GetType()
|
||||
|
||||
|
@ -107,7 +107,12 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
if keyType != keyring.TypeLocal {
|
||||
if err := migrator.Import(keyName, legKeyInfo); err != nil {
|
||||
pubkeyArmor, err := legacyKb.ExportPubKey(keyName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := migrator.ImportPubKey(keyName, pubkeyArmor); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -127,10 +132,11 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := migrator.Import(keyName, armoredPriv); err != nil {
|
||||
if err := migrator.ImportPrivKey(keyName, armoredPriv, migratePassphrase); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cmd.Print("Migration Complete")
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package keys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -86,7 +85,7 @@ hexadecimal into bech32 cosmos prefixed format and vice versa.
|
|||
}
|
||||
|
||||
func parseKey(cmd *cobra.Command, args []string) error {
|
||||
config, _ := sdk.GetSealedConfig(context.Background())
|
||||
config, _ := sdk.GetSealedConfig(cmd.Context())
|
||||
return doParseKey(cmd, config, args)
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
|
@ -1 +1 @@
|
|||
MANIFEST-000004
|
||||
MANIFEST-000005
|
||||
|
|
|
@ -1 +1 @@
|
|||
MANIFEST-000000
|
||||
MANIFEST-000003
|
||||
|
|
|
@ -1,18 +1,30 @@
|
|||
=============== Mar 30, 2020 (CEST) ===============
|
||||
02:07:34.137606 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
02:07:34.144547 db@open opening
|
||||
02:07:34.144770 version@stat F·[] S·0B[] Sc·[]
|
||||
02:07:34.145843 db@janitor F·2 G·0
|
||||
02:07:34.145875 db@open done T·1.315251ms
|
||||
02:07:34.335635 db@close closing
|
||||
02:07:34.335736 db@close done T·98.95µs
|
||||
=============== Mar 30, 2020 (CEST) ===============
|
||||
02:08:33.239115 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
02:08:33.239264 version@stat F·[] S·0B[] Sc·[]
|
||||
02:08:33.239281 db@open opening
|
||||
02:08:33.239310 journal@recovery F·1
|
||||
02:08:33.239398 journal@recovery recovering @1
|
||||
02:08:33.322008 memdb@flush created L0@2 N·4 S·391B "cos..ess,v4":"run..nfo,v3"
|
||||
02:08:33.323091 version@stat F·[1] S·391B[391B] Sc·[0.25]
|
||||
02:08:33.421979 db@janitor F·3 G·0
|
||||
02:08:33.422153 db@open done T·182.707962ms
|
||||
=============== Feb 2, 2021 (IST) ===============
|
||||
00:03:25.348369 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
00:03:25.350695 db@open opening
|
||||
00:03:25.350888 version@stat F·[] S·0B[] Sc·[]
|
||||
00:03:25.351864 db@janitor F·2 G·0
|
||||
00:03:25.351881 db@open done T·1.169825ms
|
||||
00:03:25.351895 db@close closing
|
||||
00:03:25.351929 db@close done T·33.042µs
|
||||
=============== Feb 2, 2021 (IST) ===============
|
||||
00:03:34.450638 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
00:03:34.450722 version@stat F·[] S·0B[] Sc·[]
|
||||
00:03:34.450737 db@open opening
|
||||
00:03:34.450765 journal@recovery F·1
|
||||
00:03:34.450851 journal@recovery recovering @1
|
||||
00:03:34.451173 version@stat F·[] S·0B[] Sc·[]
|
||||
00:03:34.454278 db@janitor F·2 G·0
|
||||
00:03:34.454298 db@open done T·3.548046ms
|
||||
00:03:34.454307 db@close closing
|
||||
00:03:34.454327 db@close done T·19.017µs
|
||||
=============== Feb 2, 2021 (IST) ===============
|
||||
00:03:42.025705 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
00:03:42.025892 version@stat F·[] S·0B[] Sc·[]
|
||||
00:03:42.025907 db@open opening
|
||||
00:03:42.025943 journal@recovery F·1
|
||||
00:03:42.026790 journal@recovery recovering @2
|
||||
00:03:42.026946 version@stat F·[] S·0B[] Sc·[]
|
||||
00:03:42.031645 db@janitor F·2 G·0
|
||||
00:03:42.031661 db@open done T·5.750008ms
|
||||
00:03:42.283102 db@close closing
|
||||
00:03:42.283162 db@close done T·58.775µs
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -57,6 +57,11 @@ func (ctx Context) GetFromAddress() sdk.AccAddress {
|
|||
return ctx.FromAddress
|
||||
}
|
||||
|
||||
// GetFeeGranterAddress returns the fee granter address from the context
|
||||
func (ctx Context) GetFeeGranterAddress() sdk.AccAddress {
|
||||
return ctx.FeeGranter
|
||||
}
|
||||
|
||||
// GetFromName returns the key name for the current context.
|
||||
func (ctx Context) GetFromName() string {
|
||||
return ctx.FromName
|
||||
|
|
|
@ -50,7 +50,7 @@ func ValidatorCommand() *cobra.Command {
|
|||
page, _ := cmd.Flags().GetInt(flags.FlagPage)
|
||||
limit, _ := cmd.Flags().GetInt(flags.FlagLimit)
|
||||
|
||||
result, err := GetValidators(clientCtx, height, &page, &limit)
|
||||
result, err := GetValidators(cmd.Context(), clientCtx, height, &page, &limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -117,14 +117,14 @@ func validatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error) {
|
|||
}
|
||||
|
||||
// GetValidators from client
|
||||
func GetValidators(clientCtx client.Context, height *int64, page, limit *int) (ResultValidatorsOutput, error) {
|
||||
func GetValidators(ctx context.Context, clientCtx client.Context, height *int64, page, limit *int) (ResultValidatorsOutput, error) {
|
||||
// get the node
|
||||
node, err := clientCtx.GetNode()
|
||||
if err != nil {
|
||||
return ResultValidatorsOutput{}, err
|
||||
}
|
||||
|
||||
validatorsRes, err := node.Validators(context.Background(), height, page, limit)
|
||||
validatorsRes, err := node.Validators(ctx, height, page, limit)
|
||||
if err != nil {
|
||||
return ResultValidatorsOutput{}, err
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ func ValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
output, err := GetValidators(clientCtx, &height, &page, &limit)
|
||||
output, err := GetValidators(r.Context(), clientCtx, &height, &page, &limit)
|
||||
if rest.CheckInternalServerError(w, err) {
|
||||
return
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ func LatestValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFu
|
|||
return
|
||||
}
|
||||
|
||||
output, err := GetValidators(clientCtx, nil, &page, &limit)
|
||||
output, err := GetValidators(r.Context(), clientCtx, nil, &page, &limit)
|
||||
if rest.CheckInternalServerError(w, err) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
|
|||
}
|
||||
}
|
||||
|
||||
tx.SetFeeGranter(clientCtx.GetFeeGranterAddress())
|
||||
err = Sign(txf, clientCtx.GetFromName(), tx, true)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -42,5 +42,6 @@ type (
|
|||
SetFeeAmount(amount sdk.Coins)
|
||||
SetGasLimit(limit uint64)
|
||||
SetTimeoutHeight(height uint64)
|
||||
SetFeeGranter(feeGranter sdk.AccAddress)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
[
|
||||
{
|
||||
"account_identifier": {
|
||||
"address":"cosmos1hdmjfmqmf8ck4pv4evu0s3up0ucm0yjjqfl87e"
|
||||
"address":"cosmos158nkd0l9tyemv2crp579rmj8dg37qty8lzff88"
|
||||
},
|
||||
"currency":{
|
||||
"symbol":"stake",
|
||||
"decimals":0
|
||||
},
|
||||
"value": "999900000000"
|
||||
"value": "999990000000"
|
||||
}
|
||||
]
|
|
@ -16,12 +16,13 @@ simd init simd --chain-id testing
|
|||
simd keys add fd --keyring-backend=test
|
||||
|
||||
addr=$(simd keys show fd -a --keyring-backend=test)
|
||||
val_addr=$(simd keys show fd --keyring-backend=test --bech val -a)
|
||||
|
||||
# give the accounts some money
|
||||
simd add-genesis-account "$addr" 1000000000000stake --keyring-backend=test
|
||||
|
||||
# save configs for the daemon
|
||||
simd gentx fd --chain-id testing --keyring-backend=test
|
||||
simd gentx fd 10000000stake --chain-id testing --keyring-backend=test
|
||||
|
||||
# input genTx to the genesis file
|
||||
simd collect-gentxs
|
||||
|
@ -55,4 +56,4 @@ echo zipping data dir and saving to /tmp/data.tar.gz
|
|||
|
||||
tar -czvf /tmp/data.tar.gz /root/.simapp
|
||||
|
||||
echo new address for bootstrap.json "$addr"
|
||||
echo new address for bootstrap.json "$addr" "$val_addr"
|
||||
|
|
|
@ -94,7 +94,7 @@ staking(1){
|
|||
"account": {
|
||||
"address": "staking_account",
|
||||
"sub_account": {
|
||||
"address" : "cosmosvaloper1hdmjfmqmf8ck4pv4evu0s3up0ucm0yjj9atjj2"
|
||||
"address" : "cosmosvaloper158nkd0l9tyemv2crp579rmj8dg37qty86kaut5"
|
||||
}
|
||||
},
|
||||
"amount":{
|
||||
|
@ -134,7 +134,7 @@ staking(1){
|
|||
"account": {
|
||||
"address": "staking_account",
|
||||
"sub_account": {
|
||||
"address" : "cosmosvaloper1hdmjfmqmf8ck4pv4evu0s3up0ucm0yjj9atjj2"
|
||||
"address" : "cosmosvaloper158nkd0l9tyemv2crp579rmj8dg37qty86kaut5"
|
||||
}
|
||||
},
|
||||
"amount":{
|
||||
|
|
Binary file not shown.
|
@ -158,6 +158,8 @@ func TestUnarmorInfoBytesErrors(t *testing.T) {
|
|||
}
|
||||
|
||||
func BenchmarkBcryptGenerateFromPassword(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
passphrase := []byte("passphrase")
|
||||
for securityParam := 9; securityParam < 16; securityParam++ {
|
||||
param := securityParam
|
||||
|
|
|
@ -108,6 +108,7 @@ type Signer interface {
|
|||
type Importer interface {
|
||||
// ImportPrivKey imports ASCII armored passphrase-encrypted private keys.
|
||||
ImportPrivKey(uid, armor, passphrase string) error
|
||||
|
||||
// ImportPubKey imports ASCII armored public keys.
|
||||
ImportPubKey(uid string, armor string) error
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ func (zeroReader) Read(buf []byte) (int, error) {
|
|||
// BenchmarkKeyGeneration benchmarks the given key generation algorithm using
|
||||
// a dummy reader.
|
||||
func BenchmarkKeyGeneration(b *testing.B, generateKey func(reader io.Reader) types.PrivKey) {
|
||||
b.ReportAllocs()
|
||||
var zero zeroReader
|
||||
for i := 0; i < b.N; i++ {
|
||||
generateKey(zero)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func BenchmarkKeyGeneration(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
benchmarkKeygenWrapper := func(reader io.Reader) types.PrivKey {
|
||||
priv := genPrivKey(reader)
|
||||
return &PrivKey{Key: priv}
|
||||
|
@ -17,11 +18,13 @@ func BenchmarkKeyGeneration(b *testing.B) {
|
|||
}
|
||||
|
||||
func BenchmarkSigning(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
priv := GenPrivKey()
|
||||
benchmarking.BenchmarkSigning(b, priv)
|
||||
}
|
||||
|
||||
func BenchmarkVerification(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
priv := GenPrivKey()
|
||||
benchmarking.BenchmarkVerification(b, priv)
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ The actual implementation of `BeginBlocker` and `EndBlocker` in `./abci.go` are
|
|||
|
||||
A specificity of the `EndBlocker` is that it can return validator updates to the underlying consensus engine in the form of an [`[]abci.ValidatorUpdates`](https://tendermint.com/docs/app-dev/abci-spec.html#validatorupdate). This is the preferred way to implement custom validator changes.
|
||||
|
||||
It is possible for developers to defined the order of execution between the `BeginBlocker`/`EndBlocker` functions of each of their application's modules via the module's manager `SetOrderBeginBlocker`/`SetOrderEndBlocker` methods. For more on the module manager, click [here](./module-manager.md#manager).
|
||||
It is possible for developers to define the order of execution between the `BeginBlocker`/`EndBlocker` functions of each of their application's modules via the module's manager `SetOrderBeginBlocker`/`SetOrderEndBlocker` methods. For more on the module manager, click [here](./module-manager.md#manager).
|
||||
|
||||
See an example implementation of `BeginBlocker` from the `distr` module:
|
||||
|
||||
|
|
|
@ -74,7 +74,8 @@ Let us go through the methods of `AppModule`:
|
|||
- `LegacyQuerierHandler(*codec.LegacyAmino)` (deprecated): Returns a [`querier`](./query-services.md#legacy-queriers) given the query `path`, in order to process the `query`.
|
||||
- `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.
|
||||
- `EndBlock(sdk.Context, abci.RequestEndBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the end 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 end of each block for this module.
|
||||
|
||||
|
||||
### Implementing the Application Module Interfaces
|
||||
|
||||
|
@ -132,7 +133,7 @@ The module manager is used throughout the application whenever an action on a co
|
|||
- `SetOrderInitGenesis(moduleNames ...string)`: Sets the order in which the [`InitGenesis`](./genesis.md#initgenesis) function of each module will be called when the application is first started. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
- `SetOrderExportGenesis(moduleNames ...string)`: Sets the order in which the [`ExportGenesis`](./genesis.md#exportgenesis) function of each module will be called in case of an export. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
- `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).
|
||||
- `SetOrderEndBlockers(moduleNames ...string)`: Sets the order in which the `EndBlock()` function of each module will be called at the end 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 legacy [`Msg`](./messages-and-queries.md#messages) and [`querier`](./query-services.md#legacy-queriers) routes.
|
||||
- `RegisterServices(cfg Configurator)`: Registers all module services.
|
||||
|
|
|
@ -108,6 +108,24 @@ Flags are added to commands directly (generally in the [module's CLI file](../bu
|
|||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L118
|
||||
|
||||
## Environment variables
|
||||
|
||||
Each flag is bound to it's respecteve named environment variable. Then name of the environment variable consist of two parts - capital case `basename` followed by flag name of the flag. `-` must be substituted with `_`. For example flag `--home` for application with basename `GAIA` is bound to `GAIA_HOME`. It allows to reduce amount of flags typed for routine operations. For example instead of:
|
||||
```sh
|
||||
gaia --home=./ --node=<node address> --chain-id="testchain-1" --keyring-backend=test tx ... --from=<key name>
|
||||
```
|
||||
this will be more convinient:
|
||||
```sh
|
||||
# define env variables in .env, .envrc etc
|
||||
GAIA_HOME=<path to home>
|
||||
GAIA_NODE=<node address>
|
||||
GAIA_CHAIN_ID="testchain-1"
|
||||
GAIA_KEYRING_BACKEND="test"
|
||||
|
||||
# and later just use
|
||||
gaia tx ... --from=<key name>
|
||||
```
|
||||
|
||||
## Configurations
|
||||
|
||||
It is vital that the root command of an application uses `PersistentPreRun()` cobra command property for executing the command, so all child commands have access to the server and client contexts. These contexts are set as their default values initially and maybe modified, scoped to the command, in their respective `PersistentPreRun()` functions. Note that the `client.Context` is typically pre-populated with "default" values that may be useful for all commands to inherit and override if necessary.
|
||||
|
|
|
@ -264,6 +264,32 @@
|
|||
|
||||
- [Msg](#cosmos.evidence.v1beta1.Msg)
|
||||
|
||||
- [cosmos/feegrant/v1beta1/feegrant.proto](#cosmos/feegrant/v1beta1/feegrant.proto)
|
||||
- [BasicFeeAllowance](#cosmos.feegrant.v1beta1.BasicFeeAllowance)
|
||||
- [Duration](#cosmos.feegrant.v1beta1.Duration)
|
||||
- [ExpiresAt](#cosmos.feegrant.v1beta1.ExpiresAt)
|
||||
- [FeeAllowanceGrant](#cosmos.feegrant.v1beta1.FeeAllowanceGrant)
|
||||
- [PeriodicFeeAllowance](#cosmos.feegrant.v1beta1.PeriodicFeeAllowance)
|
||||
|
||||
- [cosmos/feegrant/v1beta1/genesis.proto](#cosmos/feegrant/v1beta1/genesis.proto)
|
||||
- [GenesisState](#cosmos.feegrant.v1beta1.GenesisState)
|
||||
|
||||
- [cosmos/feegrant/v1beta1/query.proto](#cosmos/feegrant/v1beta1/query.proto)
|
||||
- [QueryFeeAllowanceRequest](#cosmos.feegrant.v1beta1.QueryFeeAllowanceRequest)
|
||||
- [QueryFeeAllowanceResponse](#cosmos.feegrant.v1beta1.QueryFeeAllowanceResponse)
|
||||
- [QueryFeeAllowancesRequest](#cosmos.feegrant.v1beta1.QueryFeeAllowancesRequest)
|
||||
- [QueryFeeAllowancesResponse](#cosmos.feegrant.v1beta1.QueryFeeAllowancesResponse)
|
||||
|
||||
- [Query](#cosmos.feegrant.v1beta1.Query)
|
||||
|
||||
- [cosmos/feegrant/v1beta1/tx.proto](#cosmos/feegrant/v1beta1/tx.proto)
|
||||
- [MsgGrantFeeAllowance](#cosmos.feegrant.v1beta1.MsgGrantFeeAllowance)
|
||||
- [MsgGrantFeeAllowanceResponse](#cosmos.feegrant.v1beta1.MsgGrantFeeAllowanceResponse)
|
||||
- [MsgRevokeFeeAllowance](#cosmos.feegrant.v1beta1.MsgRevokeFeeAllowance)
|
||||
- [MsgRevokeFeeAllowanceResponse](#cosmos.feegrant.v1beta1.MsgRevokeFeeAllowanceResponse)
|
||||
|
||||
- [Msg](#cosmos.feegrant.v1beta1.Msg)
|
||||
|
||||
- [cosmos/genutil/v1beta1/genesis.proto](#cosmos/genutil/v1beta1/genesis.proto)
|
||||
- [GenesisState](#cosmos.genutil.v1beta1.GenesisState)
|
||||
|
||||
|
@ -4097,6 +4123,312 @@ Msg defines the evidence Msg service.
|
|||
|
||||
|
||||
|
||||
<a name="cosmos/feegrant/v1beta1/feegrant.proto"></a>
|
||||
<p align="right"><a href="#top">Top</a></p>
|
||||
|
||||
## cosmos/feegrant/v1beta1/feegrant.proto
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.BasicFeeAllowance"></a>
|
||||
|
||||
### BasicFeeAllowance
|
||||
BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens
|
||||
that optionally expires. The delegatee can use up to SpendLimit to cover fees.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `spend_limit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | spend_limit specifies the maximum amount of tokens that can be spent by this allowance and will be updated as tokens are spent. If it is empty, there is no spend limit and any amount of coins can be spent. |
|
||||
| `expiration` | [ExpiresAt](#cosmos.feegrant.v1beta1.ExpiresAt) | | expiration specifies an optional time when this allowance expires |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.Duration"></a>
|
||||
|
||||
### Duration
|
||||
Duration is a span of a clock time or number of blocks.
|
||||
This is designed to be added to an ExpiresAt struct.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `duration` | [google.protobuf.Duration](#google.protobuf.Duration) | | |
|
||||
| `blocks` | [uint64](#uint64) | | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.ExpiresAt"></a>
|
||||
|
||||
### ExpiresAt
|
||||
ExpiresAt is a point in time where something expires.
|
||||
It may be *either* block time or block height
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | |
|
||||
| `height` | [int64](#int64) | | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.FeeAllowanceGrant"></a>
|
||||
|
||||
### FeeAllowanceGrant
|
||||
FeeAllowanceGrant is stored in the KVStore to record a grant with full context
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `granter` | [string](#string) | | |
|
||||
| `grantee` | [string](#string) | | |
|
||||
| `allowance` | [google.protobuf.Any](#google.protobuf.Any) | | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.PeriodicFeeAllowance"></a>
|
||||
|
||||
### PeriodicFeeAllowance
|
||||
PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap,
|
||||
as well as a limit per time period.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `basic` | [BasicFeeAllowance](#cosmos.feegrant.v1beta1.BasicFeeAllowance) | | basic specifies a struct of `BasicFeeAllowance` |
|
||||
| `period` | [Duration](#cosmos.feegrant.v1beta1.Duration) | | period specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset |
|
||||
| `period_spend_limit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | period_spend_limit specifies the maximum number of coins that can be spent in the period |
|
||||
| `period_can_spend` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | period_can_spend is the number of coins left to be spent before the period_reset time |
|
||||
| `period_reset` | [ExpiresAt](#cosmos.feegrant.v1beta1.ExpiresAt) | | period_reset is the time at which this period resets and a new one begins, it is calculated from the start time of the first transaction after the last period ended |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- end messages -->
|
||||
|
||||
<!-- end enums -->
|
||||
|
||||
<!-- end HasExtensions -->
|
||||
|
||||
<!-- end services -->
|
||||
|
||||
|
||||
|
||||
<a name="cosmos/feegrant/v1beta1/genesis.proto"></a>
|
||||
<p align="right"><a href="#top">Top</a></p>
|
||||
|
||||
## cosmos/feegrant/v1beta1/genesis.proto
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.GenesisState"></a>
|
||||
|
||||
### GenesisState
|
||||
GenesisState contains a set of fee allowances, persisted from the store
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `fee_allowances` | [FeeAllowanceGrant](#cosmos.feegrant.v1beta1.FeeAllowanceGrant) | repeated | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- end messages -->
|
||||
|
||||
<!-- end enums -->
|
||||
|
||||
<!-- end HasExtensions -->
|
||||
|
||||
<!-- end services -->
|
||||
|
||||
|
||||
|
||||
<a name="cosmos/feegrant/v1beta1/query.proto"></a>
|
||||
<p align="right"><a href="#top">Top</a></p>
|
||||
|
||||
## cosmos/feegrant/v1beta1/query.proto
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.QueryFeeAllowanceRequest"></a>
|
||||
|
||||
### QueryFeeAllowanceRequest
|
||||
QueryFeeAllowanceRequest is the request type for the Query/FeeAllowance RPC method.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `granter` | [string](#string) | | |
|
||||
| `grantee` | [string](#string) | | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.QueryFeeAllowanceResponse"></a>
|
||||
|
||||
### QueryFeeAllowanceResponse
|
||||
QueryFeeAllowanceResponse is the response type for the Query/FeeAllowance RPC method.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `fee_allowance` | [FeeAllowanceGrant](#cosmos.feegrant.v1beta1.FeeAllowanceGrant) | | fee_allowance is a fee_allowance granted for grantee by granter. |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.QueryFeeAllowancesRequest"></a>
|
||||
|
||||
### QueryFeeAllowancesRequest
|
||||
QueryFeeAllowancesRequest is the request type for the Query/FeeAllowances RPC method.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `grantee` | [string](#string) | | |
|
||||
| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | pagination defines an pagination for the request. |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.QueryFeeAllowancesResponse"></a>
|
||||
|
||||
### QueryFeeAllowancesResponse
|
||||
QueryFeeAllowancesResponse is the response type for the Query/FeeAllowances RPC method.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `fee_allowances` | [FeeAllowanceGrant](#cosmos.feegrant.v1beta1.FeeAllowanceGrant) | repeated | fee_allowances are fee_allowance's granted for grantee by granter. |
|
||||
| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines an pagination for the response. |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- end messages -->
|
||||
|
||||
<!-- end enums -->
|
||||
|
||||
<!-- end HasExtensions -->
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.Query"></a>
|
||||
|
||||
### Query
|
||||
Query defines the gRPC querier service.
|
||||
|
||||
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
|
||||
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
|
||||
| `FeeAllowance` | [QueryFeeAllowanceRequest](#cosmos.feegrant.v1beta1.QueryFeeAllowanceRequest) | [QueryFeeAllowanceResponse](#cosmos.feegrant.v1beta1.QueryFeeAllowanceResponse) | FeeAllowance returns fee granted to the grantee by the granter. | GET|/cosmos/feegrant/v1beta1/fee_allowance/{granter}/{grantee}|
|
||||
| `FeeAllowances` | [QueryFeeAllowancesRequest](#cosmos.feegrant.v1beta1.QueryFeeAllowancesRequest) | [QueryFeeAllowancesResponse](#cosmos.feegrant.v1beta1.QueryFeeAllowancesResponse) | FeeAllowances returns all the grants for address. | GET|/cosmos/feegrant/v1beta1/fee_allowances/{grantee}|
|
||||
|
||||
<!-- end services -->
|
||||
|
||||
|
||||
|
||||
<a name="cosmos/feegrant/v1beta1/tx.proto"></a>
|
||||
<p align="right"><a href="#top">Top</a></p>
|
||||
|
||||
## cosmos/feegrant/v1beta1/tx.proto
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.MsgGrantFeeAllowance"></a>
|
||||
|
||||
### MsgGrantFeeAllowance
|
||||
MsgGrantFeeAllowance adds permission for Grantee to spend up to Allowance
|
||||
of fees from the account of Granter.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `granter` | [string](#string) | | |
|
||||
| `grantee` | [string](#string) | | |
|
||||
| `allowance` | [google.protobuf.Any](#google.protobuf.Any) | | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.MsgGrantFeeAllowanceResponse"></a>
|
||||
|
||||
### MsgGrantFeeAllowanceResponse
|
||||
MsgGrantFeeAllowanceResponse defines the Msg/GrantFeeAllowanceResponse response type.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.MsgRevokeFeeAllowance"></a>
|
||||
|
||||
### MsgRevokeFeeAllowance
|
||||
MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `granter` | [string](#string) | | |
|
||||
| `grantee` | [string](#string) | | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.MsgRevokeFeeAllowanceResponse"></a>
|
||||
|
||||
### MsgRevokeFeeAllowanceResponse
|
||||
MsgRevokeFeeAllowanceResponse defines the Msg/RevokeFeeAllowanceResponse response type.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- end messages -->
|
||||
|
||||
<!-- end enums -->
|
||||
|
||||
<!-- end HasExtensions -->
|
||||
|
||||
|
||||
<a name="cosmos.feegrant.v1beta1.Msg"></a>
|
||||
|
||||
### Msg
|
||||
Msg defines the feegrant msg service.
|
||||
|
||||
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
|
||||
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
|
||||
| `GrantFeeAllowance` | [MsgGrantFeeAllowance](#cosmos.feegrant.v1beta1.MsgGrantFeeAllowance) | [MsgGrantFeeAllowanceResponse](#cosmos.feegrant.v1beta1.MsgGrantFeeAllowanceResponse) | GrantFeeAllowance grants fee allowance to the grantee on the granter's account with the provided expiration time. | |
|
||||
| `RevokeFeeAllowance` | [MsgRevokeFeeAllowance](#cosmos.feegrant.v1beta1.MsgRevokeFeeAllowance) | [MsgRevokeFeeAllowanceResponse](#cosmos.feegrant.v1beta1.MsgRevokeFeeAllowanceResponse) | RevokeFeeAllowance revokes any fee allowance of granter's account that has been granted to the grantee. | |
|
||||
|
||||
<!-- end services -->
|
||||
|
||||
|
||||
|
||||
<a name="cosmos/genutil/v1beta1/genesis.proto"></a>
|
||||
<p align="right"><a href="#top">Top</a></p>
|
||||
|
||||
|
@ -5375,8 +5707,8 @@ Commission defines commission parameters for a given validator.
|
|||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `commission_rates` | [CommissionRates](#cosmos.staking.v1beta1.CommissionRates) | | |
|
||||
| `update_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | |
|
||||
| `commission_rates` | [CommissionRates](#cosmos.staking.v1beta1.CommissionRates) | | commission_rates defines the initial commission rates to be used for creating a validator. |
|
||||
| `update_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | update_time is the last time the commission rate was changed. |
|
||||
|
||||
|
||||
|
||||
|
@ -5392,9 +5724,9 @@ a validator.
|
|||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `rate` | [string](#string) | | |
|
||||
| `max_rate` | [string](#string) | | |
|
||||
| `max_change_rate` | [string](#string) | | |
|
||||
| `rate` | [string](#string) | | rate is the commission rate charged to delegators, as a fraction. |
|
||||
| `max_rate` | [string](#string) | | max_rate defines the maximum commission rate which validator can ever charge, as a fraction. |
|
||||
| `max_change_rate` | [string](#string) | | max_change_rate defines the maximum daily increase of the validator commission, as a fraction. |
|
||||
|
||||
|
||||
|
||||
|
@ -5479,9 +5811,9 @@ validator.
|
|||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `delegator_address` | [string](#string) | | |
|
||||
| `validator_address` | [string](#string) | | |
|
||||
| `shares` | [string](#string) | | |
|
||||
| `delegator_address` | [string](#string) | | delegator_address is the bech32-encoded address of the delegator. |
|
||||
| `validator_address` | [string](#string) | | validator_address is the bech32-encoded address of the validator. |
|
||||
| `shares` | [string](#string) | | shares define the delegation shares received. |
|
||||
|
||||
|
||||
|
||||
|
@ -5513,11 +5845,11 @@ Description defines a validator description.
|
|||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `moniker` | [string](#string) | | |
|
||||
| `identity` | [string](#string) | | |
|
||||
| `website` | [string](#string) | | |
|
||||
| `security_contact` | [string](#string) | | |
|
||||
| `details` | [string](#string) | | |
|
||||
| `moniker` | [string](#string) | | moniker defines a human-readable name for the validator. |
|
||||
| `identity` | [string](#string) | | identity defines an optional identity signature (ex. UPort or Keybase). |
|
||||
| `website` | [string](#string) | | website defines an optional website link. |
|
||||
| `security_contact` | [string](#string) | | security_contact defines an optional email for security contact. |
|
||||
| `details` | [string](#string) | | details define other optional details. |
|
||||
|
||||
|
||||
|
||||
|
@ -5551,11 +5883,11 @@ Params defines the parameters for the staking module.
|
|||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `unbonding_time` | [google.protobuf.Duration](#google.protobuf.Duration) | | |
|
||||
| `max_validators` | [uint32](#uint32) | | |
|
||||
| `max_entries` | [uint32](#uint32) | | |
|
||||
| `historical_entries` | [uint32](#uint32) | | |
|
||||
| `bond_denom` | [string](#string) | | |
|
||||
| `unbonding_time` | [google.protobuf.Duration](#google.protobuf.Duration) | | unbonding_time is the time duration of unbonding. |
|
||||
| `max_validators` | [uint32](#uint32) | | max_validators is the maximum number of validators. |
|
||||
| `max_entries` | [uint32](#uint32) | | max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio). |
|
||||
| `historical_entries` | [uint32](#uint32) | | historical_entries is the number of historical entries to persist. |
|
||||
| `bond_denom` | [string](#string) | | bond_denom defines the bondable coin denomination. |
|
||||
|
||||
|
||||
|
||||
|
@ -5588,10 +5920,12 @@ from a particular source validator to a particular destination validator.
|
|||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `delegator_address` | [string](#string) | | |
|
||||
| `validator_src_address` | [string](#string) | | |
|
||||
| `validator_dst_address` | [string](#string) | | |
|
||||
| `entries` | [RedelegationEntry](#cosmos.staking.v1beta1.RedelegationEntry) | repeated | redelegation entries |
|
||||
| `delegator_address` | [string](#string) | | delegator_address is the bech32-encoded address of the delegator. |
|
||||
| `validator_src_address` | [string](#string) | | validator_src_address is the validator redelegation source operator address. |
|
||||
| `validator_dst_address` | [string](#string) | | validator_dst_address is the validator redelegation destination operator address. |
|
||||
| `entries` | [RedelegationEntry](#cosmos.staking.v1beta1.RedelegationEntry) | repeated | entries are the redelegation entries.
|
||||
|
||||
redelegation entries |
|
||||
|
||||
|
||||
|
||||
|
@ -5606,10 +5940,10 @@ RedelegationEntry defines a redelegation object with relevant metadata.
|
|||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `creation_height` | [int64](#int64) | | |
|
||||
| `completion_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | |
|
||||
| `initial_balance` | [string](#string) | | |
|
||||
| `shares_dst` | [string](#string) | | |
|
||||
| `creation_height` | [int64](#int64) | | creation_height defines the height which the redelegation took place. |
|
||||
| `completion_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | completion_time defines the unix time for redelegation completion. |
|
||||
| `initial_balance` | [string](#string) | | initial_balance defines the initial balance when redelegation started. |
|
||||
| `shares_dst` | [string](#string) | | shares_dst is the amount of destination-validator shares created by redelegation. |
|
||||
|
||||
|
||||
|
||||
|
@ -5661,9 +5995,11 @@ for a single validator in an time-ordered list.
|
|||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `delegator_address` | [string](#string) | | |
|
||||
| `validator_address` | [string](#string) | | |
|
||||
| `entries` | [UnbondingDelegationEntry](#cosmos.staking.v1beta1.UnbondingDelegationEntry) | repeated | unbonding delegation entries |
|
||||
| `delegator_address` | [string](#string) | | delegator_address is the bech32-encoded address of the delegator. |
|
||||
| `validator_address` | [string](#string) | | validator_address is the bech32-encoded address of the validator. |
|
||||
| `entries` | [UnbondingDelegationEntry](#cosmos.staking.v1beta1.UnbondingDelegationEntry) | repeated | entries are the unbonding delegation entries.
|
||||
|
||||
unbonding delegation entries |
|
||||
|
||||
|
||||
|
||||
|
@ -5678,10 +6014,10 @@ UnbondingDelegationEntry defines an unbonding object with relevant metadata.
|
|||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `creation_height` | [int64](#int64) | | |
|
||||
| `completion_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | |
|
||||
| `initial_balance` | [string](#string) | | |
|
||||
| `balance` | [string](#string) | | |
|
||||
| `creation_height` | [int64](#int64) | | creation_height is the height which the unbonding took place. |
|
||||
| `completion_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | completion_time is the unix time for unbonding completion. |
|
||||
| `initial_balance` | [string](#string) | | initial_balance defines the tokens initially scheduled to receive at completion. |
|
||||
| `balance` | [string](#string) | | balance defines the tokens to receive at completion. |
|
||||
|
||||
|
||||
|
||||
|
@ -5718,17 +6054,17 @@ multiplied by exchange rate.
|
|||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `operator_address` | [string](#string) | | |
|
||||
| `consensus_pubkey` | [google.protobuf.Any](#google.protobuf.Any) | | |
|
||||
| `jailed` | [bool](#bool) | | |
|
||||
| `status` | [BondStatus](#cosmos.staking.v1beta1.BondStatus) | | |
|
||||
| `tokens` | [string](#string) | | |
|
||||
| `delegator_shares` | [string](#string) | | |
|
||||
| `description` | [Description](#cosmos.staking.v1beta1.Description) | | |
|
||||
| `unbonding_height` | [int64](#int64) | | |
|
||||
| `unbonding_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | |
|
||||
| `commission` | [Commission](#cosmos.staking.v1beta1.Commission) | | |
|
||||
| `min_self_delegation` | [string](#string) | | |
|
||||
| `operator_address` | [string](#string) | | operator_address defines the address of the validator's operator; bech encoded in JSON. |
|
||||
| `consensus_pubkey` | [google.protobuf.Any](#google.protobuf.Any) | | consensus_pubkey is the consensus public key of the validator, as a Protobuf Any. |
|
||||
| `jailed` | [bool](#bool) | | jailed defined whether the validator has been jailed from bonded status or not. |
|
||||
| `status` | [BondStatus](#cosmos.staking.v1beta1.BondStatus) | | status is the validator status (bonded/unbonding/unbonded). |
|
||||
| `tokens` | [string](#string) | | tokens define the delegated tokens (incl. self-delegation). |
|
||||
| `delegator_shares` | [string](#string) | | delegator_shares defines total shares issued to a validator's delegators. |
|
||||
| `description` | [Description](#cosmos.staking.v1beta1.Description) | | description defines the description terms for the validator. |
|
||||
| `unbonding_height` | [int64](#int64) | | unbonding_height defines, if unbonding, the height at which this validator has begun unbonding. |
|
||||
| `unbonding_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. |
|
||||
| `commission` | [Commission](#cosmos.staking.v1beta1.Commission) | | commission defines the commission parameters. |
|
||||
| `min_self_delegation` | [string](#string) | | min_self_delegation is the validator's self declared minimum self delegation. |
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ There are multiple ways to interact with a node: using the CLI, using gRPC or us
|
|||
Now that your chain is running, it is time to try sending tokens from the first account you created to a second account. In a new terminal window, start by running the following query command:
|
||||
|
||||
```bash
|
||||
simd query account $MY_VALIDATOR_ADDRESS --chain-id my-test-chain
|
||||
simd query bank balances $MY_VALIDATOR_ADDRESS --chain-id my-test-chain
|
||||
```
|
||||
|
||||
You should see the current balance of the account you created, equal to the original balance of `stake` you granted it minus the amount you delegated via the `gentx`. Now, create a second account:
|
||||
|
@ -31,16 +31,16 @@ RECIPIENT=$(simd keys show recipient -a --keyring-backend test)
|
|||
The command above creates a local key-pair that is not yet registered on the chain. An account is created the first time it receives tokens from another account. Now, run the following command to send tokens to the `recipient` account:
|
||||
|
||||
```bash
|
||||
simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain
|
||||
simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000000stake --chain-id my-test-chain --keyring-backend test
|
||||
|
||||
# Check that the recipient account did receive the tokens.
|
||||
simd query account $RECIPIENT --chain-id my-test-chain
|
||||
simd query bank balances $RECIPIENT --chain-id my-test-chain
|
||||
```
|
||||
|
||||
Finally, delegate some of the stake tokens sent to the `recipient` account to the validator:
|
||||
|
||||
```bash
|
||||
simd tx staking delegate $(simd keys show my_validator --bech val -a --keyring-backend test) 500stake --from recipient --chain-id my-test-chain
|
||||
simd tx staking delegate $(simd keys show my_validator --bech val -a --keyring-backend test) 500stake --from recipient --chain-id my-test-chain --keyring-backend test
|
||||
|
||||
# Query the total delegations to `validator`.
|
||||
simd query staking delegations-to $(simd keys show my_validator --bech val -a --keyring-backend test) --chain-id my-test-chain
|
||||
|
|
|
@ -44,7 +44,7 @@ Before starting the chain, you need to populate the state with at least one acco
|
|||
Now that you have created a local account, go ahead and grant it some `stake` tokens in your chain's genesis file. Doing so will also make sure your chain is aware of this account's existence:
|
||||
|
||||
```bash
|
||||
simd add-genesis-account $MY_VALIDATOR_ADDRESS 100000000stake
|
||||
simd add-genesis-account $MY_VALIDATOR_ADDRESS 100000000000stake
|
||||
```
|
||||
|
||||
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.
|
||||
|
@ -53,7 +53,7 @@ Now that your account has some tokens, you need to add a validator to your chain
|
|||
|
||||
```bash
|
||||
# Create a gentx.
|
||||
simd gentx my_validator 100000stake --chain-id my-test-chain --keyring-backend test
|
||||
simd gentx my_validator 100000000stake --chain-id my-test-chain --keyring-backend test
|
||||
|
||||
# Add the gentx to the genesis file.
|
||||
simd collect-gentxs
|
||||
|
|
|
@ -11,7 +11,7 @@ This document describes how to generate an (unsigned) transaction, signing it (w
|
|||
The easiest way to send transactions is using the CLI, as we have seen in the previous page when [interacting with a node](./interact-node.md#using-the-cli). For example, running the following command
|
||||
|
||||
```bash
|
||||
simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain
|
||||
simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain --keyring-backend test
|
||||
```
|
||||
|
||||
will run the following steps:
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
syntax = "proto3";
|
||||
package cosmos.feegrant.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
import "cosmos/base/v1beta1/coin.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/duration.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types";
|
||||
|
||||
// BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens
|
||||
// that optionally expires. The delegatee can use up to SpendLimit to cover fees.
|
||||
message BasicFeeAllowance {
|
||||
option (cosmos_proto.implements_interface) = "FeeAllowanceI";
|
||||
|
||||
// spend_limit specifies the maximum amount of tokens that can be spent
|
||||
// by this allowance and will be updated as tokens are spent. If it is
|
||||
// empty, there is no spend limit and any amount of coins can be spent.
|
||||
repeated cosmos.base.v1beta1.Coin spend_limit = 1
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
|
||||
// expiration specifies an optional time when this allowance expires
|
||||
ExpiresAt expiration = 2 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap,
|
||||
// as well as a limit per time period.
|
||||
message PeriodicFeeAllowance {
|
||||
option (cosmos_proto.implements_interface) = "FeeAllowanceI";
|
||||
|
||||
// basic specifies a struct of `BasicFeeAllowance`
|
||||
BasicFeeAllowance basic = 1 [(gogoproto.nullable) = false];
|
||||
|
||||
// period specifies the time duration in which period_spend_limit coins can
|
||||
// be spent before that allowance is reset
|
||||
Duration period = 2 [(gogoproto.nullable) = false];
|
||||
|
||||
// period_spend_limit specifies the maximum number of coins that can be spent
|
||||
// in the period
|
||||
repeated cosmos.base.v1beta1.Coin period_spend_limit = 3
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
|
||||
// period_can_spend is the number of coins left to be spent before the period_reset time
|
||||
repeated cosmos.base.v1beta1.Coin period_can_spend = 4
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
|
||||
// period_reset is the time at which this period resets and a new one begins,
|
||||
// it is calculated from the start time of the first transaction after the
|
||||
// last period ended
|
||||
ExpiresAt period_reset = 5 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// Duration is a span of a clock time or number of blocks.
|
||||
// This is designed to be added to an ExpiresAt struct.
|
||||
message Duration {
|
||||
// sum is the oneof that represents either duration or block
|
||||
oneof sum {
|
||||
google.protobuf.Duration duration = 1 [(gogoproto.stdduration) = true];
|
||||
uint64 blocks = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// ExpiresAt is a point in time where something expires.
|
||||
// It may be *either* block time or block height
|
||||
message ExpiresAt {
|
||||
// sum is the oneof that represents either time or height
|
||||
oneof sum {
|
||||
google.protobuf.Timestamp time = 1 [(gogoproto.stdtime) = true];
|
||||
int64 height = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// FeeAllowanceGrant is stored in the KVStore to record a grant with full context
|
||||
message FeeAllowanceGrant {
|
||||
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
google.protobuf.Any allowance = 3 [(cosmos_proto.accepts_interface) = "FeeAllowanceI"];
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
syntax = "proto3";
|
||||
package cosmos.feegrant.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "cosmos/feegrant/v1beta1/feegrant.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types";
|
||||
|
||||
// GenesisState contains a set of fee allowances, persisted from the store
|
||||
message GenesisState {
|
||||
repeated FeeAllowanceGrant fee_allowances = 1 [(gogoproto.nullable) = false];
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
syntax = "proto3";
|
||||
package cosmos.feegrant.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "cosmos/feegrant/v1beta1/feegrant.proto";
|
||||
import "cosmos/base/query/v1beta1/pagination.proto";
|
||||
import "google/api/annotations.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types";
|
||||
|
||||
// Query defines the gRPC querier service.
|
||||
service Query {
|
||||
|
||||
// FeeAllowance returns fee granted to the grantee by the granter.
|
||||
rpc FeeAllowance(QueryFeeAllowanceRequest) returns (QueryFeeAllowanceResponse) {
|
||||
option (google.api.http).get = "/cosmos/feegrant/v1beta1/fee_allowance/{granter}/{grantee}";
|
||||
}
|
||||
|
||||
// FeeAllowances returns all the grants for address.
|
||||
rpc FeeAllowances(QueryFeeAllowancesRequest) returns (QueryFeeAllowancesResponse) {
|
||||
option (google.api.http).get = "/cosmos/feegrant/v1beta1/fee_allowances/{grantee}";
|
||||
}
|
||||
}
|
||||
|
||||
// QueryFeeAllowanceRequest is the request type for the Query/FeeAllowance RPC method.
|
||||
message QueryFeeAllowanceRequest {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
}
|
||||
|
||||
// QueryFeeAllowanceResponse is the response type for the Query/FeeAllowance RPC method.
|
||||
message QueryFeeAllowanceResponse {
|
||||
// fee_allowance is a fee_allowance granted for grantee by granter.
|
||||
cosmos.feegrant.v1beta1.FeeAllowanceGrant fee_allowance = 1;
|
||||
}
|
||||
|
||||
// QueryFeeAllowancesRequest is the request type for the Query/FeeAllowances RPC method.
|
||||
message QueryFeeAllowancesRequest {
|
||||
string grantee = 1;
|
||||
|
||||
// pagination defines an pagination for the request.
|
||||
cosmos.base.query.v1beta1.PageRequest pagination = 2;
|
||||
}
|
||||
|
||||
// QueryFeeAllowancesResponse is the response type for the Query/FeeAllowances RPC method.
|
||||
message QueryFeeAllowancesResponse {
|
||||
// fee_allowances are fee_allowance's granted for grantee by granter.
|
||||
repeated cosmos.feegrant.v1beta1.FeeAllowanceGrant fee_allowances = 1;
|
||||
|
||||
// pagination defines an pagination for the response.
|
||||
cosmos.base.query.v1beta1.PageResponse pagination = 2;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
syntax = "proto3";
|
||||
package cosmos.feegrant.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types";
|
||||
|
||||
// Msg defines the feegrant msg service.
|
||||
service Msg {
|
||||
|
||||
// GrantFeeAllowance grants fee allowance to the grantee on the granter's
|
||||
// account with the provided expiration time.
|
||||
rpc GrantFeeAllowance(MsgGrantFeeAllowance) returns (MsgGrantFeeAllowanceResponse);
|
||||
|
||||
// RevokeFeeAllowance revokes any fee allowance of granter's account that
|
||||
// has been granted to the grantee.
|
||||
rpc RevokeFeeAllowance(MsgRevokeFeeAllowance) returns (MsgRevokeFeeAllowanceResponse);
|
||||
}
|
||||
|
||||
// MsgGrantFeeAllowance adds permission for Grantee to spend up to Allowance
|
||||
// of fees from the account of Granter.
|
||||
message MsgGrantFeeAllowance {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
google.protobuf.Any allowance = 3 [(cosmos_proto.accepts_interface) = "FeeAllowanceI"];
|
||||
}
|
||||
|
||||
// MsgGrantFeeAllowanceResponse defines the Msg/GrantFeeAllowanceResponse response type.
|
||||
message MsgGrantFeeAllowanceResponse {}
|
||||
|
||||
// MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee.
|
||||
message MsgRevokeFeeAllowance {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
}
|
||||
|
||||
// MsgRevokeFeeAllowanceResponse defines the Msg/RevokeFeeAllowanceResponse response type.
|
||||
message MsgRevokeFeeAllowanceResponse {}
|
|
@ -27,12 +27,15 @@ message CommissionRates {
|
|||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// rate is the commission rate charged to delegators, as a fraction.
|
||||
string rate = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
|
||||
// max_rate defines the maximum commission rate which validator can ever charge, as a fraction.
|
||||
string max_rate = 2 [
|
||||
(gogoproto.moretags) = "yaml:\"max_rate\"",
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
|
||||
(gogoproto.nullable) = false
|
||||
];
|
||||
// max_change_rate defines the maximum daily increase of the validator commission, as a fraction.
|
||||
string max_change_rate = 3 [
|
||||
(gogoproto.moretags) = "yaml:\"max_change_rate\"",
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
|
||||
|
@ -45,7 +48,9 @@ message Commission {
|
|||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// commission_rates defines the initial commission rates to be used for creating a validator.
|
||||
CommissionRates commission_rates = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
|
||||
// update_time is the last time the commission rate was changed.
|
||||
google.protobuf.Timestamp update_time = 2
|
||||
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"update_time\""];
|
||||
}
|
||||
|
@ -55,10 +60,15 @@ message Description {
|
|||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// moniker defines a human-readable name for the validator.
|
||||
string moniker = 1;
|
||||
// identity defines an optional identity signature (ex. UPort or Keybase).
|
||||
string identity = 2;
|
||||
// website defines an optional website link.
|
||||
string website = 3;
|
||||
// security_contact defines an optional email for security contact.
|
||||
string security_contact = 4 [(gogoproto.moretags) = "yaml:\"security_contact\""];
|
||||
// details define other optional details.
|
||||
string details = 5;
|
||||
}
|
||||
|
||||
|
@ -75,22 +85,33 @@ message Validator {
|
|||
option (gogoproto.goproto_stringer) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
// operator_address defines the address of the validator's operator; bech encoded in JSON.
|
||||
string operator_address = 1 [(gogoproto.moretags) = "yaml:\"operator_address\""];
|
||||
// consensus_pubkey is the consensus public key of the validator, as a Protobuf Any.
|
||||
google.protobuf.Any consensus_pubkey = 2
|
||||
[(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey", (gogoproto.moretags) = "yaml:\"consensus_pubkey\""];
|
||||
// jailed defined whether the validator has been jailed from bonded status or not.
|
||||
bool jailed = 3;
|
||||
// status is the validator status (bonded/unbonding/unbonded).
|
||||
BondStatus status = 4;
|
||||
// tokens define the delegated tokens (incl. self-delegation).
|
||||
string tokens = 5 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false];
|
||||
// delegator_shares defines total shares issued to a validator's delegators.
|
||||
string delegator_shares = 6 [
|
||||
(gogoproto.moretags) = "yaml:\"delegator_shares\"",
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
|
||||
(gogoproto.nullable) = false
|
||||
];
|
||||
// description defines the description terms for the validator.
|
||||
Description description = 7 [(gogoproto.nullable) = false];
|
||||
// unbonding_height defines, if unbonding, the height at which this validator has begun unbonding.
|
||||
int64 unbonding_height = 8 [(gogoproto.moretags) = "yaml:\"unbonding_height\""];
|
||||
// unbonding_time defines, if unbonding, the min time for the validator to complete unbonding.
|
||||
google.protobuf.Timestamp unbonding_time = 9
|
||||
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"unbonding_time\""];
|
||||
// commission defines the commission parameters.
|
||||
Commission commission = 10 [(gogoproto.nullable) = false];
|
||||
// min_self_delegation is the validator's self declared minimum self delegation.
|
||||
string min_self_delegation = 11 [
|
||||
(gogoproto.moretags) = "yaml:\"min_self_delegation\"",
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
|
||||
|
@ -164,8 +185,11 @@ message Delegation {
|
|||
option (gogoproto.goproto_getters) = false;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// delegator_address is the bech32-encoded address of the delegator.
|
||||
string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""];
|
||||
// validator_address is the bech32-encoded address of the validator.
|
||||
string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""];
|
||||
// shares define the delegation shares received.
|
||||
string shares = 3 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
|
@ -176,8 +200,11 @@ message UnbondingDelegation {
|
|||
option (gogoproto.goproto_getters) = false;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// delegator_address is the bech32-encoded address of the delegator.
|
||||
string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""];
|
||||
// validator_address is the bech32-encoded address of the validator.
|
||||
string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""];
|
||||
// entries are the unbonding delegation entries.
|
||||
repeated UnbondingDelegationEntry entries = 3 [(gogoproto.nullable) = false]; // unbonding delegation entries
|
||||
}
|
||||
|
||||
|
@ -186,14 +213,18 @@ message UnbondingDelegationEntry {
|
|||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// creation_height is the height which the unbonding took place.
|
||||
int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""];
|
||||
// completion_time is the unix time for unbonding completion.
|
||||
google.protobuf.Timestamp completion_time = 2
|
||||
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"completion_time\""];
|
||||
// initial_balance defines the tokens initially scheduled to receive at completion.
|
||||
string initial_balance = 3 [
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.moretags) = "yaml:\"initial_balance\""
|
||||
];
|
||||
// balance defines the tokens to receive at completion.
|
||||
string balance = 4 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
|
@ -202,14 +233,18 @@ message RedelegationEntry {
|
|||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// creation_height defines the height which the redelegation took place.
|
||||
int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""];
|
||||
// completion_time defines the unix time for redelegation completion.
|
||||
google.protobuf.Timestamp completion_time = 2
|
||||
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"completion_time\""];
|
||||
// initial_balance defines the initial balance when redelegation started.
|
||||
string initial_balance = 3 [
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.moretags) = "yaml:\"initial_balance\""
|
||||
];
|
||||
// shares_dst is the amount of destination-validator shares created by redelegation.
|
||||
string shares_dst = 4
|
||||
[(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
|
||||
}
|
||||
|
@ -221,9 +256,13 @@ message Redelegation {
|
|||
option (gogoproto.goproto_getters) = false;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// delegator_address is the bech32-encoded address of the delegator.
|
||||
string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""];
|
||||
// validator_src_address is the validator redelegation source operator address.
|
||||
string validator_src_address = 2 [(gogoproto.moretags) = "yaml:\"validator_src_address\""];
|
||||
// validator_dst_address is the validator redelegation destination operator address.
|
||||
string validator_dst_address = 3 [(gogoproto.moretags) = "yaml:\"validator_dst_address\""];
|
||||
// entries are the redelegation entries.
|
||||
repeated RedelegationEntry entries = 4 [(gogoproto.nullable) = false]; // redelegation entries
|
||||
}
|
||||
|
||||
|
@ -232,12 +271,18 @@ message Params {
|
|||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// unbonding_time is the time duration of unbonding.
|
||||
google.protobuf.Duration unbonding_time = 1
|
||||
[(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (gogoproto.moretags) = "yaml:\"unbonding_time\""];
|
||||
// max_validators is the maximum number of validators.
|
||||
uint32 max_validators = 2 [(gogoproto.moretags) = "yaml:\"max_validators\""];
|
||||
// max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio).
|
||||
uint32 max_entries = 3 [(gogoproto.moretags) = "yaml:\"max_entries\""];
|
||||
// historical_entries is the number of historical entries to persist.
|
||||
uint32 historical_entries = 4 [(gogoproto.moretags) = "yaml:\"historical_entries\""];
|
||||
// bond_denom defines the bondable coin denomination.
|
||||
string bond_denom = 5 [(gogoproto.moretags) = "yaml:\"bond_denom\""];
|
||||
// power_reduction is the amount of staking tokens required for 1 unit of consensus-engine power
|
||||
string power_reduction = 6 [
|
||||
(gogoproto.moretags) = "yaml:\"power_reduction\"",
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
|
||||
|
|
|
@ -28,7 +28,7 @@ func ShowNodeIDCmd() *cobra.Command {
|
|||
serverCtx := GetServerContextFromCmd(cmd)
|
||||
cfg := serverCtx.Config
|
||||
|
||||
nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile())
|
||||
nodeKey, err := p2p.LoadNodeKey(cfg.NodeKeyFile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ func ShowValidatorCmd() *cobra.Command {
|
|||
serverCtx := GetServerContextFromCmd(cmd)
|
||||
cfg := serverCtx.Config
|
||||
|
||||
privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
||||
privValidator := pvm.LoadFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
||||
valPubKey, err := privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -86,7 +86,7 @@ func ShowAddressCmd() *cobra.Command {
|
|||
serverCtx := GetServerContextFromCmd(cmd)
|
||||
cfg := serverCtx.Config
|
||||
|
||||
privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
||||
privValidator := pvm.LoadFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
||||
valConsAddr := (sdk.ConsAddress)(privValidator.GetAddress())
|
||||
|
||||
output, _ := cmd.Flags().GetString(cli.OutputFlag)
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
tmcfg "github.com/tendermint/tendermint/config"
|
||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
||||
|
@ -63,6 +64,37 @@ func NewContext(v *viper.Viper, config *tmcfg.Config, logger tmlog.Logger) *Cont
|
|||
return &Context{v, config, logger}
|
||||
}
|
||||
|
||||
func bindFlags(basename string, cmd *cobra.Command, v *viper.Viper) (err error) {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
|
||||
cmd.Flags().VisitAll(func(f *pflag.Flag) {
|
||||
// Environment variables can't have dashes in them, so bind them to their equivalent
|
||||
// keys with underscores, e.g. --favorite-color to STING_FAVORITE_COLOR
|
||||
err = v.BindEnv(f.Name, fmt.Sprintf("%s_%s", basename, strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_"))))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = v.BindPFlag(f.Name, f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Apply the viper config value to the flag when the flag is not set and viper has a value
|
||||
if !f.Changed && v.IsSet(f.Name) {
|
||||
val := v.Get(f.Name)
|
||||
err = cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// InterceptConfigsPreRunHandler performs a pre-run function for the root daemon
|
||||
// application command. It will create a Viper literal and a default server
|
||||
// Context. The server Tendermint configuration will either be read and parsed
|
||||
|
@ -98,6 +130,9 @@ func InterceptConfigsPreRunHandler(cmd *cobra.Command) error {
|
|||
|
||||
// return value is a tendermint configuration object
|
||||
serverCtx.Config = config
|
||||
if err = bindFlags(basename, cmd, serverCtx.Viper); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var logWriter io.Writer
|
||||
if strings.ToLower(serverCtx.Viper.GetString(flags.FlagLogFormat)) == tmcfg.LogFormatPlain {
|
||||
|
|
|
@ -55,6 +55,10 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/evidence"
|
||||
evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper"
|
||||
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
|
||||
feegrant "github.com/cosmos/cosmos-sdk/x/feegrant"
|
||||
feegrantante "github.com/cosmos/cosmos-sdk/x/feegrant/ante"
|
||||
feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper"
|
||||
feegranttypes "github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
|
@ -120,6 +124,7 @@ var (
|
|||
crisis.AppModuleBasic{},
|
||||
slashing.AppModuleBasic{},
|
||||
ibc.AppModuleBasic{},
|
||||
feegrant.AppModuleBasic{},
|
||||
upgrade.AppModuleBasic{},
|
||||
evidence.AppModuleBasic{},
|
||||
transfer.AppModuleBasic{},
|
||||
|
@ -181,6 +186,7 @@ type SimApp struct {
|
|||
IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly
|
||||
EvidenceKeeper evidencekeeper.Keeper
|
||||
TransferKeeper ibctransferkeeper.Keeper
|
||||
FeeGrantKeeper feegrantkeeper.Keeper
|
||||
|
||||
// make scoped keepers public for test purposes
|
||||
ScopedIBCKeeper capabilitykeeper.ScopedKeeper
|
||||
|
@ -222,7 +228,7 @@ func NewSimApp(
|
|||
keys := sdk.NewKVStoreKeys(
|
||||
authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey,
|
||||
minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey,
|
||||
govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey,
|
||||
govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, feegranttypes.StoreKey,
|
||||
evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey,
|
||||
authztypes.StoreKey,
|
||||
)
|
||||
|
@ -277,6 +283,8 @@ func NewSimApp(
|
|||
app.CrisisKeeper = crisiskeeper.NewKeeper(
|
||||
app.GetSubspace(crisistypes.ModuleName), invCheckPeriod, app.BankKeeper, authtypes.FeeCollectorName,
|
||||
)
|
||||
|
||||
app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, keys[feegranttypes.StoreKey], app.AccountKeeper)
|
||||
app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath)
|
||||
|
||||
// register the staking hooks
|
||||
|
@ -347,6 +355,7 @@ func NewSimApp(
|
|||
bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper),
|
||||
capability.NewAppModule(appCodec, *app.CapabilityKeeper),
|
||||
crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants),
|
||||
feegrant.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry),
|
||||
gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper),
|
||||
mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper),
|
||||
slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper),
|
||||
|
@ -379,6 +388,7 @@ func NewSimApp(
|
|||
capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName,
|
||||
slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName,
|
||||
ibchost.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authztypes.ModuleName, ibctransfertypes.ModuleName,
|
||||
feegranttypes.ModuleName,
|
||||
)
|
||||
|
||||
app.mm.RegisterInvariants(&app.CrisisKeeper)
|
||||
|
@ -396,6 +406,7 @@ func NewSimApp(
|
|||
auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts),
|
||||
bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper),
|
||||
capability.NewAppModule(appCodec, *app.CapabilityKeeper),
|
||||
feegrant.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry),
|
||||
gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper),
|
||||
mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper),
|
||||
staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper),
|
||||
|
@ -419,8 +430,8 @@ func NewSimApp(
|
|||
app.SetInitChainer(app.InitChainer)
|
||||
app.SetBeginBlocker(app.BeginBlocker)
|
||||
app.SetAnteHandler(
|
||||
ante.NewAnteHandler(
|
||||
app.AccountKeeper, app.BankKeeper, ante.DefaultSigVerificationGasConsumer,
|
||||
feegrantante.NewAnteHandler(
|
||||
app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, ante.DefaultSigVerificationGasConsumer,
|
||||
encodingConfig.TxConfig.SignModeHandler(),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -157,7 +157,7 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []
|
|||
counter := int16(0)
|
||||
|
||||
for ; iter.Valid(); iter.Next() {
|
||||
addr := sdk.ValAddress(iter.Key()[1:])
|
||||
addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key()))
|
||||
validator, found := app.StakingKeeper.GetValidator(ctx, addr)
|
||||
if !found {
|
||||
panic("expected validator, not found")
|
||||
|
|
|
@ -20,4 +20,8 @@ const (
|
|||
DefaultWeightCommunitySpendProposal int = 5
|
||||
DefaultWeightTextProposal int = 5
|
||||
DefaultWeightParamChangeProposal int = 5
|
||||
|
||||
// feegrant
|
||||
DefaultWeightGrantFeeAllowance int = 100
|
||||
DefaultWeightRevokeFeeAllowance int = 100
|
||||
)
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
// Profile with:
|
||||
// /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/cosmos-sdk/simapp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out
|
||||
func BenchmarkFullAppSimulation(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
config, db, dir, logger, _, err := SetupSimulation("goleveldb-app-sim", "Simulation")
|
||||
if err != nil {
|
||||
b.Fatalf("simulation setup failed: %s", err.Error())
|
||||
|
@ -57,6 +58,7 @@ func BenchmarkFullAppSimulation(b *testing.B) {
|
|||
}
|
||||
|
||||
func BenchmarkInvariants(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
config, db, dir, logger, _, err := SetupSimulation("leveldb-app-invariant-bench", "Simulation")
|
||||
if err != nil {
|
||||
b.Fatalf("simulation setup failed: %s", err.Error())
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -100,7 +101,10 @@ func (m *mockSnapshotter) Snapshot(height uint64, format uint32) (<-chan io.Read
|
|||
// setupBusyManager creates a manager with an empty store that is busy creating a snapshot at height 1.
|
||||
// The snapshot will complete when the returned closer is called.
|
||||
func setupBusyManager(t *testing.T) *snapshots.Manager {
|
||||
tempdir := t.TempDir()
|
||||
tempdir, err := ioutil.TempDir("", "")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = os.RemoveAll(tempdir) })
|
||||
|
||||
store, err := snapshots.NewStore(db.NewMemDB(), tempdir)
|
||||
require.NoError(t, err)
|
||||
hung := newHungSnapshotter()
|
||||
|
|
|
@ -91,7 +91,7 @@ func (s *Store) Get(height uint64, format uint32) (*types.Snapshot, error) {
|
|||
|
||||
// Get fetches the latest snapshot from the database, if any.
|
||||
func (s *Store) GetLatest() (*types.Snapshot, error) {
|
||||
iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(math.MaxUint64, math.MaxUint32))
|
||||
iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32))
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(err, "failed to find latest snapshot")
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ func (s *Store) GetLatest() (*types.Snapshot, error) {
|
|||
|
||||
// List lists snapshots, in reverse order (newest first).
|
||||
func (s *Store) List() ([]*types.Snapshot, error) {
|
||||
iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(math.MaxUint64, math.MaxUint32))
|
||||
iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32))
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(err, "failed to list snapshots")
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ func (s *Store) loadChunkFile(height uint64, format uint32, chunk uint32) (io.Re
|
|||
|
||||
// Prune removes old snapshots. The given number of most recent heights (regardless of format) are retained.
|
||||
func (s *Store) Prune(retain uint32) (uint64, error) {
|
||||
iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(math.MaxUint64, math.MaxUint32))
|
||||
iter, err := s.db.ReverseIterator(encodeKey(0, 0), encodeKey(uint64(math.MaxUint64), math.MaxUint32))
|
||||
if err != nil {
|
||||
return 0, sdkerrors.Wrap(err, "failed to prune snapshots")
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -19,7 +20,10 @@ import (
|
|||
)
|
||||
|
||||
func setupStore(t *testing.T) *snapshots.Store {
|
||||
tempdir := t.TempDir()
|
||||
tempdir, err := ioutil.TempDir("", "")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = os.RemoveAll(tempdir) })
|
||||
|
||||
store, err := snapshots.NewStore(db.NewMemDB(), tempdir)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ func populate(mgr *CommitKVStoreCacheManager) {
|
|||
}
|
||||
|
||||
func BenchmarkReset(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
mgr := freshMgr()
|
||||
|
||||
b.ResetTimer()
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
)
|
||||
|
||||
func benchmarkCacheKVStoreIterator(numKVs int, b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
mem := dbadapter.Store{DB: dbm.NewMemDB()}
|
||||
cstore := cachekv.NewStore(mem)
|
||||
keys := make([]string, numKVs)
|
||||
|
|
|
@ -516,6 +516,7 @@ func (krc *keyRangeCounter) key() int {
|
|||
func bz(s string) []byte { return []byte(s) }
|
||||
|
||||
func BenchmarkCacheKVStoreGetNoKeyFound(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
st := newCacheKVStore()
|
||||
b.ResetTimer()
|
||||
// assumes b.N < 2**24
|
||||
|
@ -525,6 +526,7 @@ func BenchmarkCacheKVStoreGetNoKeyFound(b *testing.B) {
|
|||
}
|
||||
|
||||
func BenchmarkCacheKVStoreGetKeyFound(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
st := newCacheKVStore()
|
||||
for i := 0; i < b.N; i++ {
|
||||
arr := []byte{byte((i & 0xFF0000) >> 16), byte((i & 0xFF00) >> 8), byte(i & 0xFF)}
|
||||
|
|
|
@ -556,6 +556,7 @@ func TestIAVLStoreQuery(t *testing.T) {
|
|||
}
|
||||
|
||||
func BenchmarkIAVLIteratorNext(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
db := dbm.NewMemDB()
|
||||
treeSize := 1000
|
||||
tree, err := iavl.NewMutableTree(db, cacheSize)
|
||||
|
|
|
@ -711,9 +711,9 @@ func (rs *Store) Restore(
|
|||
if height == 0 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrLogic, "cannot restore snapshot at height 0")
|
||||
}
|
||||
if height > math.MaxInt64 {
|
||||
if height > uint64(math.MaxInt64) {
|
||||
return sdkerrors.Wrapf(snapshottypes.ErrInvalidMetadata,
|
||||
"snapshot height %v cannot exceed %v", height, math.MaxInt64)
|
||||
"snapshot height %v cannot exceed %v", height, int64(math.MaxInt64))
|
||||
}
|
||||
|
||||
// Signal readiness. Must be done before the readers below are set up, since the zlib
|
||||
|
|
|
@ -689,6 +689,7 @@ func BenchmarkMultistoreSnapshotRestore1M(b *testing.B) {
|
|||
}
|
||||
|
||||
func benchmarkMultistoreSnapshot(b *testing.B, stores uint8, storeKeys uint64) {
|
||||
b.ReportAllocs()
|
||||
b.StopTimer()
|
||||
source := newMultiStoreWithGeneratedData(dbm.NewMemDB(), stores, storeKeys)
|
||||
version := source.LastCommitID().Version
|
||||
|
@ -716,6 +717,7 @@ func benchmarkMultistoreSnapshot(b *testing.B, stores uint8, storeKeys uint64) {
|
|||
}
|
||||
|
||||
func benchmarkMultistoreSnapshotRestore(b *testing.B, stores uint8, storeKeys uint64) {
|
||||
b.ReportAllocs()
|
||||
b.StopTimer()
|
||||
source := newMultiStoreWithGeneratedData(dbm.NewMemDB(), stores, storeKeys)
|
||||
version := uint64(source.LastCommitID().Version)
|
||||
|
|
|
@ -12,7 +12,9 @@ import (
|
|||
|
||||
"github.com/cosmos/cosmos-sdk/codec/legacy"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/address"
|
||||
"github.com/cosmos/cosmos-sdk/types/bech32"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -28,8 +30,6 @@ const (
|
|||
// config.SetFullFundraiserPath(yourFullFundraiserPath)
|
||||
// config.Seal()
|
||||
|
||||
// AddrLen defines a valid address length
|
||||
AddrLen = 20
|
||||
// Bech32MainPrefix defines the main SDK Bech32 prefix of an account's address
|
||||
Bech32MainPrefix = "cosmos"
|
||||
|
||||
|
@ -110,9 +110,15 @@ func VerifyAddressFormat(bz []byte) error {
|
|||
if verifier != nil {
|
||||
return verifier(bz)
|
||||
}
|
||||
if len(bz) != AddrLen {
|
||||
return fmt.Errorf("incorrect address length (expected: %d, actual: %d)", AddrLen, len(bz))
|
||||
|
||||
if len(bz) == 0 {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnknownAddress, "addresses cannot be empty")
|
||||
}
|
||||
|
||||
if len(bz) > address.MaxAddrLen {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "address max length is %d, got %d", address.MaxAddrLen, len(bz))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package address
|
||||
|
||||
import (
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// MaxAddrLen is the maximum allowed length (in bytes) for an address.
|
||||
const MaxAddrLen = 255
|
||||
|
||||
// LengthPrefix prefixes the address bytes with its length, this is used
|
||||
// for example for variable-length components in store keys.
|
||||
func LengthPrefix(bz []byte) ([]byte, error) {
|
||||
bzLen := len(bz)
|
||||
if bzLen == 0 {
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
if bzLen > MaxAddrLen {
|
||||
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "address length should be max %d bytes, got %d", MaxAddrLen, bzLen)
|
||||
}
|
||||
|
||||
return append([]byte{byte(bzLen)}, bz...), nil
|
||||
}
|
||||
|
||||
// MustLengthPrefix is LengthPrefix with panic on error.
|
||||
func MustLengthPrefix(bz []byte) []byte {
|
||||
res, err := LengthPrefix(bz)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package address_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/address"
|
||||
)
|
||||
|
||||
func TestLengthPrefixedAddressStoreKey(t *testing.T) {
|
||||
addr10byte := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
addr20byte := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
|
||||
addr256byte := make([]byte, 256)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
addr []byte
|
||||
expStoreKey []byte
|
||||
expErr bool
|
||||
}{
|
||||
{"10-byte address", addr10byte, append([]byte{byte(10)}, addr10byte...), false},
|
||||
{"20-byte address", addr20byte, append([]byte{byte(20)}, addr20byte...), false},
|
||||
{"256-byte address (too long)", addr256byte, nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
storeKey, err := address.LengthPrefix(tt.addr)
|
||||
if tt.expErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expStoreKey, storeKey)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ import (
|
|||
)
|
||||
|
||||
func BenchmarkBech32ifyPubKey(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
pkBz := make([]byte, ed25519.PubKeySize)
|
||||
pk := &ed25519.PubKey{Key: pkBz}
|
||||
rng := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
|
@ -29,6 +30,7 @@ func BenchmarkBech32ifyPubKey(b *testing.B) {
|
|||
}
|
||||
|
||||
func BenchmarkGetPubKeyFromBech32(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
pkBz := make([]byte, ed25519.PubKeySize)
|
||||
pk := &ed25519.PubKey{Key: pkBz}
|
||||
rng := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
|
|
|
@ -347,15 +347,18 @@ func (s *addressTestSuite) TestVerifyAddressFormat() {
|
|||
addr5 := make([]byte, 5)
|
||||
addr20 := make([]byte, 20)
|
||||
addr32 := make([]byte, 32)
|
||||
addr256 := make([]byte, 256)
|
||||
|
||||
err := types.VerifyAddressFormat(addr0)
|
||||
s.Require().EqualError(err, "incorrect address length 0")
|
||||
s.Require().EqualError(err, "addresses cannot be empty: unknown address")
|
||||
err = types.VerifyAddressFormat(addr5)
|
||||
s.Require().EqualError(err, "incorrect address length 5")
|
||||
s.Require().NoError(err)
|
||||
err = types.VerifyAddressFormat(addr20)
|
||||
s.Require().Nil(err)
|
||||
s.Require().NoError(err)
|
||||
err = types.VerifyAddressFormat(addr32)
|
||||
s.Require().EqualError(err, "incorrect address length 32")
|
||||
s.Require().NoError(err)
|
||||
err = types.VerifyAddressFormat(addr256)
|
||||
s.Require().EqualError(err, "address max length is 255, got 256: unknown address")
|
||||
}
|
||||
|
||||
func (s *addressTestSuite) TestCustomAddressVerifier() {
|
||||
|
@ -364,34 +367,39 @@ func (s *addressTestSuite) TestCustomAddressVerifier() {
|
|||
accBech := types.AccAddress(addr).String()
|
||||
valBech := types.ValAddress(addr).String()
|
||||
consBech := types.ConsAddress(addr).String()
|
||||
// Verifiy that the default logic rejects this 10 byte address
|
||||
// Verify that the default logic doesn't reject this 10 byte address
|
||||
// The default verifier is nil, we're only checking address length is
|
||||
// between 1-255 bytes.
|
||||
err := types.VerifyAddressFormat(addr)
|
||||
s.Require().NotNil(err)
|
||||
s.Require().Nil(err)
|
||||
_, err = types.AccAddressFromBech32(accBech)
|
||||
s.Require().NotNil(err)
|
||||
s.Require().Nil(err)
|
||||
_, err = types.ValAddressFromBech32(valBech)
|
||||
s.Require().NotNil(err)
|
||||
s.Require().Nil(err)
|
||||
_, err = types.ConsAddressFromBech32(consBech)
|
||||
s.Require().NotNil(err)
|
||||
s.Require().Nil(err)
|
||||
|
||||
// Set a custom address verifier that accepts 10 or 20 byte addresses
|
||||
// Set a custom address verifier only accepts 20 byte addresses
|
||||
types.GetConfig().SetAddressVerifier(func(bz []byte) error {
|
||||
n := len(bz)
|
||||
if n == 10 || n == types.AddrLen {
|
||||
if n == 20 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("incorrect address length %d", n)
|
||||
})
|
||||
|
||||
// Verifiy that the custom logic accepts this 10 byte address
|
||||
// Verifiy that the custom logic rejects this 10 byte address
|
||||
err = types.VerifyAddressFormat(addr)
|
||||
s.Require().Nil(err)
|
||||
s.Require().NotNil(err)
|
||||
_, err = types.AccAddressFromBech32(accBech)
|
||||
s.Require().Nil(err)
|
||||
s.Require().NotNil(err)
|
||||
_, err = types.ValAddressFromBech32(valBech)
|
||||
s.Require().Nil(err)
|
||||
s.Require().NotNil(err)
|
||||
_, err = types.ConsAddressFromBech32(consBech)
|
||||
s.Require().Nil(err)
|
||||
s.Require().NotNil(err)
|
||||
|
||||
// Reinitialize the global config to default address verifier (nil)
|
||||
types.GetConfig().SetAddressVerifier(nil)
|
||||
}
|
||||
|
||||
func (s *addressTestSuite) TestBech32ifyAddressBytes() {
|
||||
|
|
|
@ -6,10 +6,11 @@ import (
|
|||
)
|
||||
|
||||
func coinName(suffix int) string {
|
||||
return fmt.Sprintf("COINZ_%d", suffix)
|
||||
return fmt.Sprintf("coinz%d", suffix)
|
||||
}
|
||||
|
||||
func BenchmarkCoinsAdditionIntersect(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
benchmarkingFunc := func(numCoinsA int, numCoinsB int) func(b *testing.B) {
|
||||
return func(b *testing.B) {
|
||||
coinsA := Coins(make([]Coin, numCoinsA))
|
||||
|
@ -39,6 +40,7 @@ func BenchmarkCoinsAdditionIntersect(b *testing.B) {
|
|||
}
|
||||
|
||||
func BenchmarkCoinsAdditionNoIntersect(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
benchmarkingFunc := func(numCoinsA int, numCoinsB int) func(b *testing.B) {
|
||||
return func(b *testing.B) {
|
||||
coinsA := Coins(make([]Coin, numCoinsA))
|
||||
|
|
|
@ -487,6 +487,7 @@ func (s *decimalTestSuite) TestOperationOrders() {
|
|||
}
|
||||
|
||||
func BenchmarkMarshalTo(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
bis := []struct {
|
||||
in sdk.Dec
|
||||
want []byte
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/address"
|
||||
"github.com/cosmos/cosmos-sdk/types/query"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
|
@ -111,7 +112,7 @@ func ExampleFilteredPaginate() {
|
|||
pageReq := &query.PageRequest{Key: nil, Limit: 1, CountTotal: true}
|
||||
store := ctx.KVStore(app.GetKey(authtypes.StoreKey))
|
||||
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
||||
accountStore := prefix.NewStore(balancesStore, addr1.Bytes())
|
||||
accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1))
|
||||
|
||||
var balResult sdk.Coins
|
||||
pageRes, err := query.FilteredPaginate(accountStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) {
|
||||
|
@ -143,7 +144,7 @@ func ExampleFilteredPaginate() {
|
|||
|
||||
func execFilterPaginate(store sdk.KVStore, pageReq *query.PageRequest, appCodec codec.Marshaler) (balances sdk.Coins, res *query.PageResponse, err error) {
|
||||
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
||||
accountStore := prefix.NewStore(balancesStore, addr1.Bytes())
|
||||
accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1))
|
||||
|
||||
var balResult sdk.Coins
|
||||
res, err = query.FilteredPaginate(accountStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) {
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/store"
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/address"
|
||||
"github.com/cosmos/cosmos-sdk/types/query"
|
||||
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
|
@ -193,7 +194,7 @@ func ExamplePaginate() {
|
|||
balResult := sdk.NewCoins()
|
||||
authStore := ctx.KVStore(app.GetKey(authtypes.StoreKey))
|
||||
balancesStore := prefix.NewStore(authStore, types.BalancesPrefix)
|
||||
accountStore := prefix.NewStore(balancesStore, addr1.Bytes())
|
||||
accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1))
|
||||
pageRes, err := query.Paginate(accountStore, request.Pagination, func(key []byte, value []byte) error {
|
||||
var tempRes sdk.Coin
|
||||
err := app.AppCodec().UnmarshalBinaryBare(value, &tempRes)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
@ -58,7 +57,7 @@ $ <appd> query auth params
|
|||
}
|
||||
|
||||
queryClient := types.NewQueryClient(clientCtx)
|
||||
res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{})
|
||||
res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -90,7 +89,7 @@ func GetAccountCmd() *cobra.Command {
|
|||
}
|
||||
|
||||
queryClient := types.NewQueryClient(clientCtx)
|
||||
res, err := queryClient.Account(context.Background(), &types.QueryAccountRequest{Address: key.String()})
|
||||
res, err := queryClient.Account(cmd.Context(), &types.QueryAccountRequest{Address: key.String()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
)
|
||||
|
||||
func BenchmarkAccountMapperGetAccountFound(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
app, ctx := createTestApp(false)
|
||||
|
||||
// assumes b.N < 2**24
|
||||
|
|
|
@ -84,6 +84,9 @@ func (s *StdTxBuilder) SetTimeoutHeight(height uint64) {
|
|||
s.TimeoutHeight = height
|
||||
}
|
||||
|
||||
// SetFeeGranter does nothing for stdtx
|
||||
func (s *StdTxBuilder) SetFeeGranter(_ sdk.AccAddress) {}
|
||||
|
||||
// StdTxConfig is a context.TxConfig for StdTx
|
||||
type StdTxConfig struct {
|
||||
Cdc *codec.LegacyAmino
|
||||
|
|
|
@ -2,6 +2,7 @@ package types
|
|||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/address"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -21,7 +22,7 @@ const (
|
|||
// Keys for authz store
|
||||
// Items are stored with the following key: values
|
||||
//
|
||||
// - 0x01<granterAddress_Bytes><granteeAddress_Bytes><msgType_Bytes>: Grant
|
||||
// - 0x01<granterAddressLen (1 Byte)><granterAddress_Bytes><granteeAddressLen (1 Byte)><granteeAddress_Bytes><msgType_Bytes>: Grant
|
||||
|
||||
var (
|
||||
// Keys for store prefixes
|
||||
|
@ -30,12 +31,22 @@ var (
|
|||
|
||||
// GetAuthorizationStoreKey - return authorization store key
|
||||
func GetAuthorizationStoreKey(grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) []byte {
|
||||
return append(append(append(GrantKey, granter.Bytes()...), grantee.Bytes()...), []byte(msgType)...)
|
||||
return append(append(append(
|
||||
GrantKey,
|
||||
address.MustLengthPrefix(granter)...),
|
||||
address.MustLengthPrefix(grantee)...),
|
||||
[]byte(msgType)...,
|
||||
)
|
||||
}
|
||||
|
||||
// ExtractAddressesFromGrantKey - split granter & grantee address from the authorization key
|
||||
func ExtractAddressesFromGrantKey(key []byte) (granterAddr, granteeAddr sdk.AccAddress) {
|
||||
granterAddr = sdk.AccAddress(key[1 : sdk.AddrLen+1])
|
||||
granteeAddr = sdk.AccAddress(key[sdk.AddrLen+1 : sdk.AddrLen*2+1])
|
||||
// key if of format:
|
||||
// 0x01<granterAddressLen (1 Byte)><granterAddress_Bytes><granteeAddressLen (1 Byte)><granteeAddress_Bytes><msgType_Bytes>
|
||||
granterAddrLen := key[1] // remove prefix key
|
||||
granterAddr = sdk.AccAddress(key[2 : 2+granterAddrLen])
|
||||
granteeAddrLen := int(key[2+granterAddrLen])
|
||||
granteeAddr = sdk.AccAddress(key[3+granterAddrLen : 3+granterAddrLen+byte(granteeAddrLen)])
|
||||
|
||||
return granterAddr, granteeAddr
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
var moduleAccAddr = authtypes.NewModuleAddress(stakingtypes.BondedPoolName)
|
||||
|
||||
func BenchmarkOneBankSendTxPerBlock(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
// Add an account at genesis
|
||||
acc := authtypes.BaseAccount{
|
||||
Address: addr1.String(),
|
||||
|
|
|
@ -74,11 +74,10 @@ Example:
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := cmd.Context()
|
||||
if denom == "" {
|
||||
params := types.NewQueryAllBalancesRequest(addr, pageReq)
|
||||
|
||||
res, err := queryClient.AllBalances(cmd.Context(), params)
|
||||
res, err := queryClient.AllBalances(ctx, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -86,7 +85,7 @@ Example:
|
|||
}
|
||||
|
||||
params := types.NewQueryBalanceRequest(addr, denom)
|
||||
res, err := queryClient.Balance(cmd.Context(), params)
|
||||
res, err := queryClient.Balance(ctx, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -183,9 +182,9 @@ To query for the total supply of a specific coin denomination use:
|
|||
}
|
||||
|
||||
queryClient := types.NewQueryClient(clientCtx)
|
||||
|
||||
ctx := cmd.Context()
|
||||
if denom == "" {
|
||||
res, err := queryClient.TotalSupply(cmd.Context(), &types.QueryTotalSupplyRequest{})
|
||||
res, err := queryClient.TotalSupply(ctx, &types.QueryTotalSupplyRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -193,7 +192,7 @@ To query for the total supply of a specific coin denomination use:
|
|||
return clientCtx.PrintProto(res)
|
||||
}
|
||||
|
||||
res, err := queryClient.SupplyOf(cmd.Context(), &types.QuerySupplyOfRequest{Denom: denom})
|
||||
res, err := queryClient.SupplyOf(ctx, &types.QuerySupplyOfRequest{Denom: denom})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package testutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -59,7 +58,7 @@ ignored as it is implied from [from_key_or_address].`,
|
|||
msg := types.NewMsgSend(clientCtx.GetFromAddress(), toAddr, coins)
|
||||
svcMsgClientConn := &msgservice.ServiceMsgClientConn{}
|
||||
bankMsgClient := types.NewMsgClient(svcMsgClientConn)
|
||||
_, err = bankMsgClient.Send(context.Background(), msg)
|
||||
_, err = bankMsgClient.Send(cmd.Context(), msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -57,9 +57,7 @@ func (k BaseKeeper) AllBalances(ctx context.Context, req *types.QueryAllBalances
|
|||
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
||||
|
||||
balances := sdk.NewCoins()
|
||||
store := sdkCtx.KVStore(k.storeKey)
|
||||
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
||||
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
|
||||
accountStore := k.getAccountStore(sdkCtx, addr)
|
||||
|
||||
pageRes, err := query.Paginate(accountStore, req.Pagination, func(_, value []byte) error {
|
||||
var result sdk.Coin
|
||||
|
|
|
@ -2,7 +2,6 @@ package keeper
|
|||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
"github.com/cosmos/cosmos-sdk/telemetry"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
@ -233,9 +232,7 @@ func (k BaseSendKeeper) ClearBalances(ctx sdk.Context, addr sdk.AccAddress) {
|
|||
return false
|
||||
})
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
||||
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
|
||||
accountStore := k.getAccountStore(ctx, addr)
|
||||
|
||||
for _, key := range keys {
|
||||
accountStore.Delete(key)
|
||||
|
@ -264,9 +261,7 @@ func (k BaseSendKeeper) SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance
|
|||
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, balance.String())
|
||||
}
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
||||
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
|
||||
accountStore := k.getAccountStore(ctx, addr)
|
||||
|
||||
bz := k.cdc.MustMarshalBinaryBare(&balance)
|
||||
accountStore.Set([]byte(balance.Denom), bz)
|
||||
|
|
|
@ -97,9 +97,7 @@ func (k BaseViewKeeper) GetAccountsBalances(ctx sdk.Context) []types.Balance {
|
|||
// GetBalance returns the balance of a specific denomination for a given account
|
||||
// by address.
|
||||
func (k BaseViewKeeper) GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
||||
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
|
||||
accountStore := k.getAccountStore(ctx, addr)
|
||||
|
||||
bz := accountStore.Get([]byte(denom))
|
||||
if bz == nil {
|
||||
|
@ -116,9 +114,7 @@ func (k BaseViewKeeper) GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom s
|
|||
// provides the token balance to a callback. If true is returned from the
|
||||
// callback, iteration is halted.
|
||||
func (k BaseViewKeeper) IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(sdk.Coin) bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
|
||||
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
|
||||
accountStore := k.getAccountStore(ctx, addr)
|
||||
|
||||
iterator := accountStore.Iterator(nil, nil)
|
||||
defer iterator.Close()
|
||||
|
@ -214,3 +210,10 @@ func (k BaseViewKeeper) ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) er
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getAccountStore gets the account store of the given address.
|
||||
func (k BaseViewKeeper) getAccountStore(ctx sdk.Context, addr sdk.AccAddress) prefix.Store {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
||||
return prefix.NewStore(store, types.CreateAccountBalancesPrefix(addr))
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/address"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -22,7 +21,9 @@ const (
|
|||
|
||||
// KVStore keys
|
||||
var (
|
||||
BalancesPrefix = []byte("balances")
|
||||
// BalancesPrefix is the for the account balances store. We use a byte
|
||||
// (instead of say `[]]byte("balances")` to save some disk space).
|
||||
BalancesPrefix = []byte{0x02}
|
||||
SupplyKey = []byte{0x00}
|
||||
DenomMetadataPrefix = []byte{0x1}
|
||||
)
|
||||
|
@ -37,10 +38,13 @@ func DenomMetadataKey(denom string) []byte {
|
|||
// store. The key must not contain the perfix BalancesPrefix as the prefix store
|
||||
// iterator discards the actual prefix.
|
||||
func AddressFromBalancesStore(key []byte) sdk.AccAddress {
|
||||
addr := key[:sdk.AddrLen]
|
||||
if len(addr) != sdk.AddrLen {
|
||||
panic(fmt.Sprintf("unexpected account address key length; got: %d, expected: %d", len(addr), sdk.AddrLen))
|
||||
}
|
||||
addrLen := key[0]
|
||||
addr := key[1 : addrLen+1]
|
||||
|
||||
return sdk.AccAddress(addr)
|
||||
}
|
||||
|
||||
// CreateAccountBalancesPrefix creates the prefix for an account's balances.
|
||||
func CreateAccountBalancesPrefix(addr []byte) []byte {
|
||||
return append(BalancesPrefix, address.MustLengthPrefix(addr)...)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/address"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
)
|
||||
|
||||
|
@ -19,8 +20,10 @@ func cloneAppend(bz []byte, tail []byte) (res []byte) {
|
|||
func TestAddressFromBalancesStore(t *testing.T) {
|
||||
addr, err := sdk.AccAddressFromBech32("cosmos1n88uc38xhjgxzw9nwre4ep2c8ga4fjxcar6mn7")
|
||||
require.NoError(t, err)
|
||||
addrLen := len(addr)
|
||||
require.Equal(t, 20, addrLen)
|
||||
|
||||
key := cloneAppend(addr.Bytes(), []byte("stake"))
|
||||
key := cloneAppend(address.MustLengthPrefix(addr), []byte("stake"))
|
||||
res := types.AddressFromBalancesStore(key)
|
||||
require.Equal(t, res, addr)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Validate performs a basic validation of the coin metadata fields
|
||||
// Validate performs a basic validation of the coin metadata fields. It checks:
|
||||
// - Base and Display denominations are valid coin denominations
|
||||
// - Base and Display denominations are present in the DenomUnit slice
|
||||
// - Base denomination has exponent 0
|
||||
// - Denomination units are sorted in ascending order
|
||||
// - Denomination units not duplicated
|
||||
func (m Metadata) Validate() error {
|
||||
if err := sdk.ValidateDenom(m.Base); err != nil {
|
||||
return fmt.Errorf("invalid metadata base denom: %w", err)
|
||||
|
@ -17,12 +23,37 @@ func (m Metadata) Validate() error {
|
|||
return fmt.Errorf("invalid metadata display denom: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
hasDisplay bool
|
||||
currentExponent uint32 // check that the exponents are increasing
|
||||
)
|
||||
|
||||
seenUnits := make(map[string]bool)
|
||||
for _, denomUnit := range m.DenomUnits {
|
||||
|
||||
for i, denomUnit := range m.DenomUnits {
|
||||
// The first denomination unit MUST be the base
|
||||
if i == 0 {
|
||||
// validate denomination and exponent
|
||||
if denomUnit.Denom != m.Base {
|
||||
return fmt.Errorf("metadata's first denomination unit must be the one with base denom '%s'", m.Base)
|
||||
}
|
||||
if denomUnit.Exponent != 0 {
|
||||
return fmt.Errorf("the exponent for base denomination unit %s must be 0", m.Base)
|
||||
}
|
||||
} else if currentExponent >= denomUnit.Exponent {
|
||||
return errors.New("denom units should be sorted asc by exponent")
|
||||
}
|
||||
|
||||
currentExponent = denomUnit.Exponent
|
||||
|
||||
if seenUnits[denomUnit.Denom] {
|
||||
return fmt.Errorf("duplicate denomination unit %s", denomUnit.Denom)
|
||||
}
|
||||
|
||||
if denomUnit.Denom == m.Display {
|
||||
hasDisplay = true
|
||||
}
|
||||
|
||||
if err := denomUnit.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -30,6 +61,10 @@ func (m Metadata) Validate() error {
|
|||
seenUnits[denomUnit.Denom] = true
|
||||
}
|
||||
|
||||
if !hasDisplay {
|
||||
return fmt.Errorf("metadata must contain a denomination unit with display denom '%s'", m.Display)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ func TestMetadataValidate(t *testing.T) {
|
|||
Description: "The native staking token of the Cosmos Hub.",
|
||||
DenomUnits: []*types.DenomUnit{
|
||||
{"uatom", uint32(0), []string{"microatom"}},
|
||||
{"uatom", uint32(0), []string{"microatom"}},
|
||||
{"uatom", uint32(1), []string{"microatom"}},
|
||||
},
|
||||
Base: "uatom",
|
||||
Display: "atom",
|
||||
|
@ -94,6 +94,59 @@ func TestMetadataValidate(t *testing.T) {
|
|||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"no base denom unit",
|
||||
types.Metadata{
|
||||
Description: "The native staking token of the Cosmos Hub.",
|
||||
DenomUnits: []*types.DenomUnit{
|
||||
{"matom", uint32(3), []string{"milliatom"}},
|
||||
{"atom", uint32(6), nil},
|
||||
},
|
||||
Base: "uatom",
|
||||
Display: "atom",
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"base denom exponent not zero",
|
||||
types.Metadata{
|
||||
Description: "The native staking token of the Cosmos Hub.",
|
||||
DenomUnits: []*types.DenomUnit{
|
||||
{"uatom", uint32(1), []string{"microatom"}},
|
||||
{"matom", uint32(3), []string{"milliatom"}},
|
||||
{"atom", uint32(6), nil},
|
||||
},
|
||||
Base: "uatom",
|
||||
Display: "atom",
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"no display denom unit",
|
||||
types.Metadata{
|
||||
Description: "The native staking token of the Cosmos Hub.",
|
||||
DenomUnits: []*types.DenomUnit{
|
||||
{"uatom", uint32(0), []string{"microatom"}},
|
||||
},
|
||||
Base: "uatom",
|
||||
Display: "atom",
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"denom units not sorted",
|
||||
types.Metadata{
|
||||
Description: "The native staking token of the Cosmos Hub.",
|
||||
DenomUnits: []*types.DenomUnit{
|
||||
{"uatom", uint32(0), []string{"microatom"}},
|
||||
{"atom", uint32(6), nil},
|
||||
{"matom", uint32(3), []string{"milliatom"}},
|
||||
},
|
||||
Base: "uatom",
|
||||
Display: "atom",
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
|
|
@ -23,7 +23,7 @@ func TestMsgSendValidation(t *testing.T) {
|
|||
addr1 := sdk.AccAddress([]byte("from________________"))
|
||||
addr2 := sdk.AccAddress([]byte("to__________________"))
|
||||
addrEmpty := sdk.AccAddress([]byte(""))
|
||||
addrTooLong := sdk.AccAddress([]byte("Accidentally used 33 bytes pubkey"))
|
||||
addrLong := sdk.AccAddress([]byte("Purposefully long address"))
|
||||
|
||||
atom123 := sdk.NewCoins(sdk.NewInt64Coin("atom", 123))
|
||||
atom0 := sdk.NewCoins(sdk.NewInt64Coin("atom", 0))
|
||||
|
@ -36,12 +36,12 @@ func TestMsgSendValidation(t *testing.T) {
|
|||
}{
|
||||
{"", NewMsgSend(addr1, addr2, atom123)}, // valid send
|
||||
{"", NewMsgSend(addr1, addr2, atom123eth123)}, // valid send with multiple coins
|
||||
{"", NewMsgSend(addrLong, addr2, atom123)}, // valid send with long addr sender
|
||||
{"", NewMsgSend(addr1, addrLong, atom123)}, // valid send with long addr recipient
|
||||
{": invalid coins", NewMsgSend(addr1, addr2, atom0)}, // non positive coin
|
||||
{"123atom,0eth: invalid coins", NewMsgSend(addr1, addr2, atom123eth0)}, // non positive coin in multicoins
|
||||
{"Invalid sender address (empty address string is not allowed): invalid address", NewMsgSend(addrEmpty, addr2, atom123)},
|
||||
{"Invalid sender address (incorrect address length (expected: 20, actual: 33)): invalid address", NewMsgSend(addrTooLong, addr2, atom123)},
|
||||
{"Invalid recipient address (empty address string is not allowed): invalid address", NewMsgSend(addr1, addrEmpty, atom123)},
|
||||
{"Invalid recipient address (incorrect address length (expected: 20, actual: 33)): invalid address", NewMsgSend(addr1, addrTooLong, atom123)},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
|
@ -91,7 +91,7 @@ func TestInputValidation(t *testing.T) {
|
|||
addr1 := sdk.AccAddress([]byte("_______alice________"))
|
||||
addr2 := sdk.AccAddress([]byte("________bob_________"))
|
||||
addrEmpty := sdk.AccAddress([]byte(""))
|
||||
addrTooLong := sdk.AccAddress([]byte("Accidentally used 33 bytes pubkey"))
|
||||
addrLong := sdk.AccAddress([]byte("Purposefully long address"))
|
||||
|
||||
someCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123))
|
||||
multiCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 20))
|
||||
|
@ -109,9 +109,9 @@ func TestInputValidation(t *testing.T) {
|
|||
{"", NewInput(addr1, someCoins)},
|
||||
{"", NewInput(addr2, someCoins)},
|
||||
{"", NewInput(addr2, multiCoins)},
|
||||
{"", NewInput(addrLong, someCoins)},
|
||||
|
||||
{"empty address string is not allowed", NewInput(addrEmpty, someCoins)},
|
||||
{"incorrect address length (expected: 20, actual: 33)", NewInput(addrTooLong, someCoins)},
|
||||
{": invalid coins", NewInput(addr1, emptyCoins)}, // invalid coins
|
||||
{": invalid coins", NewInput(addr1, emptyCoins2)}, // invalid coins
|
||||
{"10eth,0atom: invalid coins", NewInput(addr1, someEmptyCoins)}, // invalid coins
|
||||
|
@ -132,7 +132,7 @@ func TestOutputValidation(t *testing.T) {
|
|||
addr1 := sdk.AccAddress([]byte("_______alice________"))
|
||||
addr2 := sdk.AccAddress([]byte("________bob_________"))
|
||||
addrEmpty := sdk.AccAddress([]byte(""))
|
||||
addrTooLong := sdk.AccAddress([]byte("Accidentally used 33 bytes pubkey"))
|
||||
addrLong := sdk.AccAddress([]byte("Purposefully long address"))
|
||||
|
||||
someCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123))
|
||||
multiCoins := sdk.NewCoins(sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 20))
|
||||
|
@ -150,9 +150,9 @@ func TestOutputValidation(t *testing.T) {
|
|||
{"", NewOutput(addr1, someCoins)},
|
||||
{"", NewOutput(addr2, someCoins)},
|
||||
{"", NewOutput(addr2, multiCoins)},
|
||||
{"", NewOutput(addrLong, someCoins)},
|
||||
|
||||
{"Invalid output address (empty address string is not allowed): invalid address", NewOutput(addrEmpty, someCoins)},
|
||||
{"Invalid output address (incorrect address length (expected: 20, actual: 33)): invalid address", NewOutput(addrTooLong, someCoins)},
|
||||
{": invalid coins", NewOutput(addr1, emptyCoins)}, // invalid coins
|
||||
{": invalid coins", NewOutput(addr1, emptyCoins2)}, // invalid coins
|
||||
{"10eth,0atom: invalid coins", NewOutput(addr1, someEmptyCoins)}, // invalid coins
|
||||
|
@ -251,8 +251,6 @@ func TestMsgMultiSendGetSigners(t *testing.T) {
|
|||
require.Equal(t, "[696E707574313131313131313131313131313131 696E707574323232323232323232323232323232 696E707574333333333333333333333333333333]", fmt.Sprintf("%v", res))
|
||||
}
|
||||
|
||||
/*
|
||||
// what to do w/ this test?
|
||||
func TestMsgSendSigners(t *testing.T) {
|
||||
signers := []sdk.AccAddress{
|
||||
{1, 2, 3},
|
||||
|
@ -265,8 +263,7 @@ func TestMsgSendSigners(t *testing.T) {
|
|||
for i, signer := range signers {
|
||||
inputs[i] = NewInput(signer, someCoins)
|
||||
}
|
||||
tx := NewMsgSend(inputs, nil)
|
||||
tx := NewMsgMultiSend(inputs, nil)
|
||||
|
||||
require.Equal(t, signers, tx.Signers())
|
||||
require.Equal(t, signers, tx.GetSigners())
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -50,7 +49,7 @@ func GetCmdQueryParams() *cobra.Command {
|
|||
}
|
||||
queryClient := types.NewQueryClient(clientCtx)
|
||||
|
||||
res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{})
|
||||
res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -94,7 +93,7 @@ $ %s query distribution validator-outstanding-rewards %s1lwjmdnks33xwnmfayc64ycp
|
|||
}
|
||||
|
||||
res, err := queryClient.ValidatorOutstandingRewards(
|
||||
context.Background(),
|
||||
cmd.Context(),
|
||||
&types.QueryValidatorOutstandingRewardsRequest{ValidatorAddress: validatorAddr.String()},
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -139,7 +138,7 @@ $ %s query distribution commission %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj
|
|||
}
|
||||
|
||||
res, err := queryClient.ValidatorCommission(
|
||||
context.Background(),
|
||||
cmd.Context(),
|
||||
&types.QueryValidatorCommissionRequest{ValidatorAddress: validatorAddr.String()},
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -199,7 +198,7 @@ $ %s query distribution slashes %svaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj
|
|||
}
|
||||
|
||||
res, err := queryClient.ValidatorSlashes(
|
||||
context.Background(),
|
||||
cmd.Context(),
|
||||
&types.QueryValidatorSlashesRequest{
|
||||
ValidatorAddress: validatorAddr.String(),
|
||||
StartingHeight: startHeight,
|
||||
|
@ -252,6 +251,7 @@ $ %s query distribution rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1ggh
|
|||
}
|
||||
|
||||
// query for rewards from a particular delegation
|
||||
ctx := cmd.Context()
|
||||
if len(args) == 2 {
|
||||
validatorAddr, err := sdk.ValAddressFromBech32(args[1])
|
||||
if err != nil {
|
||||
|
@ -259,7 +259,7 @@ $ %s query distribution rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1ggh
|
|||
}
|
||||
|
||||
res, err := queryClient.DelegationRewards(
|
||||
context.Background(),
|
||||
ctx,
|
||||
&types.QueryDelegationRewardsRequest{DelegatorAddress: delegatorAddr.String(), ValidatorAddress: validatorAddr.String()},
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -270,7 +270,7 @@ $ %s query distribution rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p %s1ggh
|
|||
}
|
||||
|
||||
res, err := queryClient.DelegationTotalRewards(
|
||||
context.Background(),
|
||||
ctx,
|
||||
&types.QueryDelegationTotalRewardsRequest{DelegatorAddress: delegatorAddr.String()},
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -307,7 +307,7 @@ $ %s query distribution community-pool
|
|||
}
|
||||
queryClient := types.NewQueryClient(clientCtx)
|
||||
|
||||
res, err := queryClient.CommunityPool(context.Background(), &types.QueryCommunityPoolRequest{})
|
||||
res, err := queryClient.CommunityPool(cmd.Context(), &types.QueryCommunityPoolRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
@ -155,7 +154,7 @@ $ %s tx distribution withdraw-all-rewards --from mykey
|
|||
}
|
||||
|
||||
queryClient := types.NewQueryClient(clientCtx)
|
||||
delValsRes, err := queryClient.DelegatorValidators(context.Background(), &types.QueryDelegatorValidatorsRequest{DelegatorAddress: delAddr.String()})
|
||||
delValsRes, err := queryClient.DelegatorValidators(cmd.Context(), &types.QueryDelegatorValidatorsRequest{DelegatorAddress: delAddr.String()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/binary"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/address"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -27,19 +28,19 @@ const (
|
|||
//
|
||||
// - 0x01: sdk.ConsAddress
|
||||
//
|
||||
// - 0x02<valAddr_Bytes>: ValidatorOutstandingRewards
|
||||
// - 0x02<valAddrLen (1 Byte)><valAddr_Bytes>: ValidatorOutstandingRewards
|
||||
//
|
||||
// - 0x03<accAddr_Bytes>: sdk.AccAddress
|
||||
// - 0x03<accAddrLen (1 Byte)><accAddr_Bytes>: sdk.AccAddress
|
||||
//
|
||||
// - 0x04<valAddr_Bytes><accAddr_Bytes>: DelegatorStartingInfo
|
||||
// - 0x04<valAddrLen (1 Byte)><valAddr_Bytes><accAddrLen (1 Byte)><accAddr_Bytes>: DelegatorStartingInfo
|
||||
//
|
||||
// - 0x05<valAddr_Bytes><period_Bytes>: ValidatorHistoricalRewards
|
||||
// - 0x05<valAddrLen (1 Byte)><valAddr_Bytes><period_Bytes>: ValidatorHistoricalRewards
|
||||
//
|
||||
// - 0x06<valAddr_Bytes>: ValidatorCurrentRewards
|
||||
// - 0x06<valAddrLen (1 Byte)><valAddr_Bytes>: ValidatorCurrentRewards
|
||||
//
|
||||
// - 0x07<valAddr_Bytes>: ValidatorCurrentRewards
|
||||
// - 0x07<valAddrLen (1 Byte)><valAddr_Bytes>: ValidatorCurrentRewards
|
||||
//
|
||||
// - 0x08<valAddr_Bytes><height>: ValidatorSlashEvent
|
||||
// - 0x08<valAddrLen (1 Byte)><valAddr_Bytes><height>: ValidatorSlashEvent
|
||||
var (
|
||||
FeePoolKey = []byte{0x00} // key for global distribution state
|
||||
ProposerKey = []byte{0x01} // key for the proposer operator address
|
||||
|
@ -53,47 +54,56 @@ var (
|
|||
ValidatorSlashEventPrefix = []byte{0x08} // key for validator slash fraction
|
||||
)
|
||||
|
||||
// gets an address from a validator's outstanding rewards key
|
||||
// GetValidatorOutstandingRewardsAddress creates an address from a validator's outstanding rewards key.
|
||||
func GetValidatorOutstandingRewardsAddress(key []byte) (valAddr sdk.ValAddress) {
|
||||
addr := key[1:]
|
||||
if len(addr) != sdk.AddrLen {
|
||||
// key is in the format:
|
||||
// 0x02<valAddrLen (1 Byte)><valAddr_Bytes>
|
||||
|
||||
// Remove prefix and address length.
|
||||
addr := key[2:]
|
||||
if len(addr) != int(key[1]) {
|
||||
panic("unexpected key length")
|
||||
}
|
||||
|
||||
return sdk.ValAddress(addr)
|
||||
}
|
||||
|
||||
// gets an address from a delegator's withdraw info key
|
||||
// GetDelegatorWithdrawInfoAddress creates an address from a delegator's withdraw info key.
|
||||
func GetDelegatorWithdrawInfoAddress(key []byte) (delAddr sdk.AccAddress) {
|
||||
addr := key[1:]
|
||||
if len(addr) != sdk.AddrLen {
|
||||
// key is in the format:
|
||||
// 0x03<accAddrLen (1 Byte)><accAddr_Bytes>
|
||||
|
||||
// Remove prefix and address length.
|
||||
addr := key[2:]
|
||||
if len(addr) != int(key[1]) {
|
||||
panic("unexpected key length")
|
||||
}
|
||||
|
||||
return sdk.AccAddress(addr)
|
||||
}
|
||||
|
||||
// gets the addresses from a delegator starting info key
|
||||
// GetDelegatorStartingInfoAddresses creates the addresses from a delegator starting info key.
|
||||
func GetDelegatorStartingInfoAddresses(key []byte) (valAddr sdk.ValAddress, delAddr sdk.AccAddress) {
|
||||
addr := key[1 : 1+sdk.AddrLen]
|
||||
if len(addr) != sdk.AddrLen {
|
||||
// key is in the format:
|
||||
// 0x04<valAddrLen (1 Byte)><valAddr_Bytes><accAddrLen (1 Byte)><accAddr_Bytes>
|
||||
valAddrLen := int(key[1])
|
||||
valAddr = sdk.ValAddress(key[2 : 2+valAddrLen])
|
||||
delAddrLen := int(key[2+valAddrLen])
|
||||
delAddr = sdk.AccAddress(key[3+valAddrLen:])
|
||||
if len(delAddr.Bytes()) != delAddrLen {
|
||||
panic("unexpected key length")
|
||||
}
|
||||
valAddr = sdk.ValAddress(addr)
|
||||
addr = key[1+sdk.AddrLen:]
|
||||
if len(addr) != sdk.AddrLen {
|
||||
panic("unexpected key length")
|
||||
}
|
||||
delAddr = sdk.AccAddress(addr)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// gets the address & period from a validator's historical rewards key
|
||||
// GetValidatorHistoricalRewardsAddressPeriod creates the address & period from a validator's historical rewards key.
|
||||
func GetValidatorHistoricalRewardsAddressPeriod(key []byte) (valAddr sdk.ValAddress, period uint64) {
|
||||
addr := key[1 : 1+sdk.AddrLen]
|
||||
if len(addr) != sdk.AddrLen {
|
||||
panic("unexpected key length")
|
||||
}
|
||||
valAddr = sdk.ValAddress(addr)
|
||||
b := key[1+sdk.AddrLen:]
|
||||
// key is in the format:
|
||||
// 0x05<valAddrLen (1 Byte)><valAddr_Bytes><period_Bytes>
|
||||
valAddrLen := int(key[1])
|
||||
valAddr = sdk.ValAddress(key[2 : 2+valAddrLen])
|
||||
b := key[2+valAddrLen:]
|
||||
if len(b) != 8 {
|
||||
panic("unexpected key length")
|
||||
}
|
||||
|
@ -101,93 +111,104 @@ func GetValidatorHistoricalRewardsAddressPeriod(key []byte) (valAddr sdk.ValAddr
|
|||
return
|
||||
}
|
||||
|
||||
// gets the address from a validator's current rewards key
|
||||
// GetValidatorCurrentRewardsAddress creates the address from a validator's current rewards key.
|
||||
func GetValidatorCurrentRewardsAddress(key []byte) (valAddr sdk.ValAddress) {
|
||||
addr := key[1:]
|
||||
if len(addr) != sdk.AddrLen {
|
||||
// key is in the format:
|
||||
// 0x06<valAddrLen (1 Byte)><valAddr_Bytes>: ValidatorCurrentRewards
|
||||
|
||||
// Remove prefix and address length.
|
||||
addr := key[2:]
|
||||
if len(addr) != int(key[1]) {
|
||||
panic("unexpected key length")
|
||||
}
|
||||
|
||||
return sdk.ValAddress(addr)
|
||||
}
|
||||
|
||||
// gets the address from a validator's accumulated commission key
|
||||
// GetValidatorAccumulatedCommissionAddress creates the address from a validator's accumulated commission key.
|
||||
func GetValidatorAccumulatedCommissionAddress(key []byte) (valAddr sdk.ValAddress) {
|
||||
addr := key[1:]
|
||||
if len(addr) != sdk.AddrLen {
|
||||
// key is in the format:
|
||||
// 0x07<valAddrLen (1 Byte)><valAddr_Bytes>: ValidatorCurrentRewards
|
||||
|
||||
// Remove prefix and address length.
|
||||
addr := key[2:]
|
||||
if len(addr) != int(key[1]) {
|
||||
panic("unexpected key length")
|
||||
}
|
||||
|
||||
return sdk.ValAddress(addr)
|
||||
}
|
||||
|
||||
// gets the height from a validator's slash event key
|
||||
// GetValidatorSlashEventAddressHeight creates the height from a validator's slash event key.
|
||||
func GetValidatorSlashEventAddressHeight(key []byte) (valAddr sdk.ValAddress, height uint64) {
|
||||
addr := key[1 : 1+sdk.AddrLen]
|
||||
if len(addr) != sdk.AddrLen {
|
||||
panic("unexpected key length")
|
||||
}
|
||||
valAddr = sdk.ValAddress(addr)
|
||||
startB := 1 + sdk.AddrLen
|
||||
// key is in the format:
|
||||
// 0x08<valAddrLen (1 Byte)><valAddr_Bytes><height>: ValidatorSlashEvent
|
||||
valAddrLen := int(key[1])
|
||||
valAddr = key[2 : 2+valAddrLen]
|
||||
startB := 2 + valAddrLen
|
||||
b := key[startB : startB+8] // the next 8 bytes represent the height
|
||||
height = binary.BigEndian.Uint64(b)
|
||||
return
|
||||
}
|
||||
|
||||
// gets the outstanding rewards key for a validator
|
||||
// GetValidatorOutstandingRewardsKey creates the outstanding rewards key for a validator.
|
||||
func GetValidatorOutstandingRewardsKey(valAddr sdk.ValAddress) []byte {
|
||||
return append(ValidatorOutstandingRewardsPrefix, valAddr.Bytes()...)
|
||||
return append(ValidatorOutstandingRewardsPrefix, address.MustLengthPrefix(valAddr.Bytes())...)
|
||||
}
|
||||
|
||||
// gets the key for a delegator's withdraw addr
|
||||
// GetDelegatorWithdrawAddrKey creates the key for a delegator's withdraw addr.
|
||||
func GetDelegatorWithdrawAddrKey(delAddr sdk.AccAddress) []byte {
|
||||
return append(DelegatorWithdrawAddrPrefix, delAddr.Bytes()...)
|
||||
return append(DelegatorWithdrawAddrPrefix, address.MustLengthPrefix(delAddr.Bytes())...)
|
||||
}
|
||||
|
||||
// gets the key for a delegator's starting info
|
||||
// GetDelegatorStartingInfoKey creates the key for a delegator's starting info.
|
||||
func GetDelegatorStartingInfoKey(v sdk.ValAddress, d sdk.AccAddress) []byte {
|
||||
return append(append(DelegatorStartingInfoPrefix, v.Bytes()...), d.Bytes()...)
|
||||
return append(append(DelegatorStartingInfoPrefix, address.MustLengthPrefix(v.Bytes())...), address.MustLengthPrefix(d.Bytes())...)
|
||||
}
|
||||
|
||||
// gets the prefix key for a validator's historical rewards
|
||||
// GetValidatorHistoricalRewardsPrefix creates the prefix key for a validator's historical rewards.
|
||||
func GetValidatorHistoricalRewardsPrefix(v sdk.ValAddress) []byte {
|
||||
return append(ValidatorHistoricalRewardsPrefix, v.Bytes()...)
|
||||
return append(ValidatorHistoricalRewardsPrefix, address.MustLengthPrefix(v.Bytes())...)
|
||||
}
|
||||
|
||||
// gets the key for a validator's historical rewards
|
||||
// GetValidatorHistoricalRewardsKey creates the key for a validator's historical rewards.
|
||||
func GetValidatorHistoricalRewardsKey(v sdk.ValAddress, k uint64) []byte {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, k)
|
||||
return append(append(ValidatorHistoricalRewardsPrefix, v.Bytes()...), b...)
|
||||
return append(append(ValidatorHistoricalRewardsPrefix, address.MustLengthPrefix(v.Bytes())...), b...)
|
||||
}
|
||||
|
||||
// gets the key for a validator's current rewards
|
||||
// GetValidatorCurrentRewardsKey creates the key for a validator's current rewards.
|
||||
func GetValidatorCurrentRewardsKey(v sdk.ValAddress) []byte {
|
||||
return append(ValidatorCurrentRewardsPrefix, v.Bytes()...)
|
||||
return append(ValidatorCurrentRewardsPrefix, address.MustLengthPrefix(v.Bytes())...)
|
||||
}
|
||||
|
||||
// gets the key for a validator's current commission
|
||||
// GetValidatorAccumulatedCommissionKey creates the key for a validator's current commission.
|
||||
func GetValidatorAccumulatedCommissionKey(v sdk.ValAddress) []byte {
|
||||
return append(ValidatorAccumulatedCommissionPrefix, v.Bytes()...)
|
||||
return append(ValidatorAccumulatedCommissionPrefix, address.MustLengthPrefix(v.Bytes())...)
|
||||
}
|
||||
|
||||
// gets the prefix key for a validator's slash fractions
|
||||
// GetValidatorSlashEventPrefix creates the prefix key for a validator's slash fractions.
|
||||
func GetValidatorSlashEventPrefix(v sdk.ValAddress) []byte {
|
||||
return append(ValidatorSlashEventPrefix, v.Bytes()...)
|
||||
return append(ValidatorSlashEventPrefix, address.MustLengthPrefix(v.Bytes())...)
|
||||
}
|
||||
|
||||
// gets the prefix key for a validator's slash fraction (ValidatorSlashEventPrefix + height)
|
||||
// GetValidatorSlashEventKeyPrefix creates the prefix key for a validator's slash fraction (ValidatorSlashEventPrefix + height).
|
||||
func GetValidatorSlashEventKeyPrefix(v sdk.ValAddress, height uint64) []byte {
|
||||
heightBz := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(heightBz, height)
|
||||
|
||||
return append(
|
||||
ValidatorSlashEventPrefix,
|
||||
append(v.Bytes(), heightBz...)...,
|
||||
append(address.MustLengthPrefix(v.Bytes()), heightBz...)...,
|
||||
)
|
||||
}
|
||||
|
||||
// gets the key for a validator's slash fraction
|
||||
// GetValidatorSlashEventKey creates the key for a validator's slash fraction.
|
||||
func GetValidatorSlashEventKey(v sdk.ValAddress, height, period uint64) []byte {
|
||||
periodBz := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(periodBz, period)
|
||||
prefix := GetValidatorSlashEventKeyPrefix(v, height)
|
||||
|
||||
return append(prefix, periodBz...)
|
||||
}
|
||||
|
|
|
@ -88,7 +88,8 @@ func (e Equivocation) GetTotalPower() int64 { return 0 }
|
|||
// FromABCIEvidence converts a Tendermint concrete Evidence type to
|
||||
// SDK Evidence using Equivocation as the concrete type.
|
||||
func FromABCIEvidence(e abci.Evidence) exported.Evidence {
|
||||
consAddr, err := sdk.Bech32ifyAddressBytes(sdk.Bech32PrefixConsAddr, e.Validator.Address)
|
||||
bech32PrefixConsAddr := sdk.GetConfig().GetBech32ConsensusAddrPrefix()
|
||||
consAddr, err := sdk.Bech32ifyAddressBytes(bech32PrefixConsAddr, e.Validator.Address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/evidence/types"
|
||||
|
@ -57,3 +58,22 @@ func TestEquivocationValidateBasic(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvidenceAddressConversion(t *testing.T) {
|
||||
sdk.GetConfig().SetBech32PrefixForConsensusNode("testcnclcons", "testcnclconspub")
|
||||
tmEvidence := abci.Evidence{
|
||||
Type: abci.EvidenceType_DUPLICATE_VOTE,
|
||||
Validator: abci.Validator{
|
||||
Address: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Power: 100,
|
||||
},
|
||||
Height: 1,
|
||||
Time: time.Now(),
|
||||
TotalVotingPower: 100,
|
||||
}
|
||||
evidence := types.FromABCIEvidence(tmEvidence).(*types.Equivocation)
|
||||
consAddr := evidence.GetConsensusAddress()
|
||||
// Check the address is the same after conversion
|
||||
require.Equal(t, tmEvidence.Validator.Address, consAddr.Bytes())
|
||||
sdk.GetConfig().SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package ante
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper"
|
||||
feegranttypes "github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
)
|
||||
|
||||
// NewAnteHandler returns an AnteHandler that checks and increments sequence
|
||||
// numbers, checks signatures & account numbers, and deducts fees from the
|
||||
// fee_payer or from fee_granter (if valid grant exist).
|
||||
func NewAnteHandler(
|
||||
ak authkeeper.AccountKeeper, bankKeeper feegranttypes.BankKeeper, feeGrantKeeper feegrantkeeper.Keeper,
|
||||
sigGasConsumer authante.SignatureVerificationGasConsumer, signModeHandler signing.SignModeHandler,
|
||||
) sdk.AnteHandler {
|
||||
|
||||
return sdk.ChainAnteDecorators(
|
||||
authante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first
|
||||
authante.NewRejectExtensionOptionsDecorator(),
|
||||
authante.NewMempoolFeeDecorator(),
|
||||
authante.NewValidateBasicDecorator(),
|
||||
authante.TxTimeoutHeightDecorator{},
|
||||
authante.NewValidateMemoDecorator(ak),
|
||||
authante.NewConsumeGasForTxSizeDecorator(ak),
|
||||
NewDeductGrantedFeeDecorator(ak, bankKeeper, feeGrantKeeper),
|
||||
authante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators
|
||||
authante.NewValidateSigCountDecorator(ak),
|
||||
authante.NewSigGasConsumeDecorator(ak, sigGasConsumer),
|
||||
authante.NewSigVerificationDecorator(ak, signModeHandler),
|
||||
authante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator
|
||||
)
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package ante
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
)
|
||||
|
||||
// DeductGrantedFeeDecorator deducts fees from fee_payer or fee_granter (if exists a valid fee allowance) of the tx
|
||||
// If the fee_payer or fee_granter does not have the funds to pay for the fees, return with InsufficientFunds error
|
||||
// Call next AnteHandler if fees successfully deducted
|
||||
// CONTRACT: Tx must implement GrantedFeeTx interface to use DeductGrantedFeeDecorator
|
||||
type DeductGrantedFeeDecorator struct {
|
||||
ak types.AccountKeeper
|
||||
k keeper.Keeper
|
||||
bk types.BankKeeper
|
||||
}
|
||||
|
||||
func NewDeductGrantedFeeDecorator(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) DeductGrantedFeeDecorator {
|
||||
return DeductGrantedFeeDecorator{
|
||||
ak: ak,
|
||||
k: k,
|
||||
bk: bk,
|
||||
}
|
||||
}
|
||||
|
||||
// AnteHandle performs a decorated ante-handler responsible for deducting transaction
|
||||
// fees. Fees will be deducted from the account designated by the FeePayer on a
|
||||
// transaction by default. However, if the fee payer differs from the transaction
|
||||
// signer, the handler will check if a fee grant has been authorized. If the
|
||||
// transaction's signer does not exist, it will be created.
|
||||
func (d DeductGrantedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
feeTx, ok := tx.(sdk.FeeTx)
|
||||
if !ok {
|
||||
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a GrantedFeeTx")
|
||||
}
|
||||
|
||||
// sanity check from DeductFeeDecorator
|
||||
if addr := d.ak.GetModuleAddress(authtypes.FeeCollectorName); addr == nil {
|
||||
panic(fmt.Sprintf("%s module account has not been set", authtypes.FeeCollectorName))
|
||||
}
|
||||
|
||||
fee := feeTx.GetFee()
|
||||
feePayer := feeTx.FeePayer()
|
||||
feeGranter := feeTx.FeeGranter()
|
||||
|
||||
deductFeesFrom := feePayer
|
||||
|
||||
// ensure the grant is allowed, if we request a different fee payer
|
||||
if feeGranter != nil && !feeGranter.Equals(feePayer) {
|
||||
err := d.k.UseGrantedFees(ctx, feeGranter, feePayer, fee)
|
||||
if err != nil {
|
||||
return ctx, sdkerrors.Wrapf(err, "%s not allowed to pay fees from %s", feeGranter, feePayer)
|
||||
}
|
||||
|
||||
deductFeesFrom = feeGranter
|
||||
}
|
||||
|
||||
// now, either way, we know that we are authorized to deduct the fees from the deductFeesFrom account
|
||||
deductFeesFromAcc := d.ak.GetAccount(ctx, deductFeesFrom)
|
||||
if deductFeesFromAcc == nil {
|
||||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", deductFeesFrom)
|
||||
}
|
||||
|
||||
// move on if there is no fee to deduct
|
||||
if fee.IsZero() {
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
||||
|
||||
// deduct fee if non-zero
|
||||
err = authante.DeductFees(d.bk, ctx, deductFeesFromAcc, fee)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
return next(ctx, tx, simulate)
|
||||
}
|
|
@ -0,0 +1,287 @@
|
|||
package ante_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
authsign "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/ante"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
)
|
||||
|
||||
// AnteTestSuite is a test suite to be used with ante handler tests.
|
||||
type AnteTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
app *simapp.SimApp
|
||||
anteHandler sdk.AnteHandler
|
||||
ctx sdk.Context
|
||||
clientCtx client.Context
|
||||
txBuilder client.TxBuilder
|
||||
}
|
||||
|
||||
// SetupTest setups a new test, with new app, context, and anteHandler.
|
||||
func (suite *AnteTestSuite) SetupTest(isCheckTx bool) {
|
||||
suite.app, suite.ctx = createTestApp(isCheckTx)
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
|
||||
// Set up TxConfig.
|
||||
encodingConfig := simapp.MakeTestEncodingConfig()
|
||||
// We're using TestMsg encoding in some tests, so register it here.
|
||||
encodingConfig.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil)
|
||||
testdata.RegisterInterfaces(encodingConfig.InterfaceRegistry)
|
||||
|
||||
suite.clientCtx = client.Context{}.
|
||||
WithTxConfig(encodingConfig.TxConfig)
|
||||
|
||||
suite.anteHandler = ante.NewAnteHandler(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.FeeGrantKeeper, authante.DefaultSigVerificationGasConsumer, encodingConfig.TxConfig.SignModeHandler())
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestDeductFeesNoDelegation() {
|
||||
suite.SetupTest(true)
|
||||
// setup
|
||||
app, ctx := suite.app, suite.ctx
|
||||
|
||||
protoTxCfg := tx.NewTxConfig(codec.NewProtoCodec(app.InterfaceRegistry()), tx.DefaultSignModes)
|
||||
|
||||
// this just tests our handler
|
||||
dfd := ante.NewDeductGrantedFeeDecorator(app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper)
|
||||
ourAnteHandler := sdk.ChainAnteDecorators(dfd)
|
||||
|
||||
// this tests the whole stack
|
||||
anteHandlerStack := suite.anteHandler
|
||||
|
||||
// keys and addresses
|
||||
priv1, _, addr1 := testdata.KeyTestPubAddr()
|
||||
priv2, _, addr2 := testdata.KeyTestPubAddr()
|
||||
priv3, _, addr3 := testdata.KeyTestPubAddr()
|
||||
priv4, _, addr4 := testdata.KeyTestPubAddr()
|
||||
priv5, _, addr5 := testdata.KeyTestPubAddr()
|
||||
|
||||
// Set addr1 with insufficient funds
|
||||
acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1)
|
||||
app.AccountKeeper.SetAccount(ctx, acc1)
|
||||
app.BankKeeper.SetBalances(ctx, addr1, []sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(10))})
|
||||
|
||||
// Set addr2 with more funds
|
||||
acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2)
|
||||
app.AccountKeeper.SetAccount(ctx, acc2)
|
||||
app.BankKeeper.SetBalances(ctx, addr2, []sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(99999))})
|
||||
|
||||
// grant fee allowance from `addr2` to `addr3` (plenty to pay)
|
||||
err := app.FeeGrantKeeper.GrantFeeAllowance(ctx, addr2, addr3, &types.BasicFeeAllowance{
|
||||
SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 500)),
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// grant low fee allowance (20atom), to check the tx requesting more than allowed.
|
||||
err = app.FeeGrantKeeper.GrantFeeAllowance(ctx, addr2, addr4, &types.BasicFeeAllowance{
|
||||
SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 20)),
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
cases := map[string]struct {
|
||||
signerKey cryptotypes.PrivKey
|
||||
signer sdk.AccAddress
|
||||
feeAccount sdk.AccAddress
|
||||
feeAccountKey cryptotypes.PrivKey
|
||||
handler sdk.AnteHandler
|
||||
fee int64
|
||||
valid bool
|
||||
}{
|
||||
"paying with low funds (only ours)": {
|
||||
signerKey: priv1,
|
||||
signer: addr1,
|
||||
fee: 50,
|
||||
handler: ourAnteHandler,
|
||||
valid: false,
|
||||
},
|
||||
"paying with good funds (only ours)": {
|
||||
signerKey: priv2,
|
||||
signer: addr2,
|
||||
fee: 50,
|
||||
handler: ourAnteHandler,
|
||||
valid: true,
|
||||
},
|
||||
"paying with no account (only ours)": {
|
||||
signerKey: priv3,
|
||||
signer: addr3,
|
||||
fee: 1,
|
||||
handler: ourAnteHandler,
|
||||
valid: false,
|
||||
},
|
||||
"no fee with real account (only ours)": {
|
||||
signerKey: priv1,
|
||||
signer: addr1,
|
||||
fee: 0,
|
||||
handler: ourAnteHandler,
|
||||
valid: true,
|
||||
},
|
||||
"no fee with no account (only ours)": {
|
||||
signerKey: priv5,
|
||||
signer: addr5,
|
||||
fee: 0,
|
||||
handler: ourAnteHandler,
|
||||
valid: false,
|
||||
},
|
||||
"valid fee grant without account (only ours)": {
|
||||
signerKey: priv3,
|
||||
signer: addr3,
|
||||
feeAccount: addr2,
|
||||
fee: 50,
|
||||
handler: ourAnteHandler,
|
||||
valid: true,
|
||||
},
|
||||
"no fee grant (only ours)": {
|
||||
signerKey: priv3,
|
||||
signer: addr3,
|
||||
feeAccount: addr1,
|
||||
fee: 2,
|
||||
handler: ourAnteHandler,
|
||||
valid: false,
|
||||
},
|
||||
"allowance smaller than requested fee (only ours)": {
|
||||
signerKey: priv4,
|
||||
signer: addr4,
|
||||
feeAccount: addr2,
|
||||
fee: 50,
|
||||
handler: ourAnteHandler,
|
||||
valid: false,
|
||||
},
|
||||
"granter cannot cover allowed fee grant (only ours)": {
|
||||
signerKey: priv4,
|
||||
signer: addr4,
|
||||
feeAccount: addr1,
|
||||
fee: 50,
|
||||
handler: ourAnteHandler,
|
||||
valid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, stc := range cases {
|
||||
tc := stc // to make scopelint happy
|
||||
suite.T().Run(name, func(t *testing.T) {
|
||||
fee := sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee))
|
||||
msgs := []sdk.Msg{testdata.NewTestMsg(tc.signer)}
|
||||
|
||||
acc := app.AccountKeeper.GetAccount(ctx, tc.signer)
|
||||
privs, accNums, seqs := []cryptotypes.PrivKey{tc.signerKey}, []uint64{0}, []uint64{0}
|
||||
if acc != nil {
|
||||
accNums, seqs = []uint64{acc.GetAccountNumber()}, []uint64{acc.GetSequence()}
|
||||
}
|
||||
|
||||
tx, err := genTxWithFeeGranter(protoTxCfg, msgs, fee, helpers.DefaultGenTxGas, ctx.ChainID(), accNums, seqs, tc.feeAccount, privs...)
|
||||
suite.Require().NoError(err)
|
||||
_, err = ourAnteHandler(ctx, tx, false)
|
||||
if tc.valid {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
|
||||
_, err = anteHandlerStack(ctx, tx, false)
|
||||
if tc.valid {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// returns context and app with params set on account keeper
|
||||
func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) {
|
||||
app := simapp.Setup(isCheckTx)
|
||||
ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{})
|
||||
app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams())
|
||||
|
||||
return app, ctx
|
||||
}
|
||||
|
||||
// don't consume any gas
|
||||
func SigGasNoConsumer(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params authtypes.Params) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func genTxWithFeeGranter(gen client.TxConfig, msgs []sdk.Msg, feeAmt sdk.Coins, gas uint64, chainID string, accNums,
|
||||
accSeqs []uint64, feeGranter sdk.AccAddress, priv ...cryptotypes.PrivKey) (sdk.Tx, error) {
|
||||
sigs := make([]signing.SignatureV2, len(priv))
|
||||
|
||||
// create a random length memo
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
memo := simulation.RandStringOfLength(r, simulation.RandIntBetween(r, 0, 100))
|
||||
|
||||
signMode := gen.SignModeHandler().DefaultMode()
|
||||
|
||||
// 1st round: set SignatureV2 with empty signatures, to set correct
|
||||
// signer infos.
|
||||
for i, p := range priv {
|
||||
sigs[i] = signing.SignatureV2{
|
||||
PubKey: p.PubKey(),
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: signMode,
|
||||
},
|
||||
Sequence: accSeqs[i],
|
||||
}
|
||||
}
|
||||
|
||||
tx := gen.NewTxBuilder()
|
||||
err := tx.SetMsgs(msgs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = tx.SetSignatures(sigs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tx.SetMemo(memo)
|
||||
tx.SetFeeAmount(feeAmt)
|
||||
tx.SetGasLimit(gas)
|
||||
tx.SetFeeGranter(feeGranter)
|
||||
|
||||
// 2nd round: once all signer infos are set, every signer can sign.
|
||||
for i, p := range priv {
|
||||
signerData := authsign.SignerData{
|
||||
ChainID: chainID,
|
||||
AccountNumber: accNums[i],
|
||||
Sequence: accSeqs[i],
|
||||
}
|
||||
signBytes, err := gen.SignModeHandler().GetSignBytes(signMode, signerData, tx.GetTx())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sig, err := p.Sign(signBytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sigs[i].Data.(*signing.SingleSignatureData).Signature = sig
|
||||
err = tx.SetSignatures(sigs...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return tx.GetTx(), nil
|
||||
}
|
||||
|
||||
func TestAnteTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(AnteTestSuite))
|
||||
}
|
|
@ -0,0 +1,621 @@
|
|||
// +build norace
|
||||
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/client/cli"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
govtestutil "github.com/cosmos/cosmos-sdk/x/gov/client/testutil"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/stretchr/testify/suite"
|
||||
tmcli "github.com/tendermint/tendermint/libs/cli"
|
||||
)
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
cfg network.Config
|
||||
network *network.Network
|
||||
addedGranter sdk.AccAddress
|
||||
addedGrantee sdk.AccAddress
|
||||
addedGrant types.FeeAllowanceGrant
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) SetupSuite() {
|
||||
s.T().Log("setting up integration test suite")
|
||||
|
||||
if testing.Short() {
|
||||
s.T().Skip("skipping test in unit-tests mode.")
|
||||
}
|
||||
|
||||
cfg := network.DefaultConfig()
|
||||
cfg.NumValidators = 2
|
||||
|
||||
s.cfg = cfg
|
||||
s.network = network.New(s.T(), cfg)
|
||||
|
||||
_, err := s.network.WaitForHeight(1)
|
||||
s.Require().NoError(err)
|
||||
|
||||
val := s.network.Validators[0]
|
||||
granter := val.Address
|
||||
grantee := s.network.Validators[1].Address
|
||||
|
||||
clientCtx := val.ClientCtx
|
||||
commonFlags := []string{
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
|
||||
}
|
||||
|
||||
fee := sdk.NewCoin("stake", sdk.NewInt(100))
|
||||
duration := 365 * 24 * 60 * 60
|
||||
|
||||
args := append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
grantee.String(),
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
fmt.Sprintf("--%s=%v", cli.FlagExpiration, duration),
|
||||
},
|
||||
commonFlags...,
|
||||
)
|
||||
|
||||
cmd := cli.NewCmdFeeGrant()
|
||||
|
||||
_, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.network.WaitForHeight(1)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.addedGranter = granter
|
||||
s.addedGrantee = grantee
|
||||
|
||||
grant, err := types.NewFeeAllowanceGrant(granter, grantee, &types.BasicFeeAllowance{
|
||||
SpendLimit: sdk.NewCoins(fee),
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.addedGrant = grant
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TearDownSuite() {
|
||||
s.T().Log("tearing down integration test suite")
|
||||
s.network.Cleanup()
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestCmdGetFeeGrant() {
|
||||
val := s.network.Validators[0]
|
||||
granter := val.Address
|
||||
grantee := s.addedGrantee
|
||||
clientCtx := val.ClientCtx
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectErrMsg string
|
||||
expectErr bool
|
||||
respType *types.FeeAllowanceGrant
|
||||
resp *types.FeeAllowanceGrant
|
||||
}{
|
||||
{
|
||||
"wrong granter",
|
||||
[]string{
|
||||
"wrong_granter",
|
||||
grantee.String(),
|
||||
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
|
||||
},
|
||||
"decoding bech32 failed",
|
||||
true, nil, nil,
|
||||
},
|
||||
{
|
||||
"wrong grantee",
|
||||
[]string{
|
||||
granter.String(),
|
||||
"wrong_grantee",
|
||||
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
|
||||
},
|
||||
"decoding bech32 failed",
|
||||
true, nil, nil,
|
||||
},
|
||||
{
|
||||
"non existed grant",
|
||||
[]string{
|
||||
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
|
||||
grantee.String(),
|
||||
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
|
||||
},
|
||||
"no fee allowance found",
|
||||
true, nil, nil,
|
||||
},
|
||||
{
|
||||
"valid req",
|
||||
[]string{
|
||||
granter.String(),
|
||||
grantee.String(),
|
||||
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
|
||||
},
|
||||
"",
|
||||
false,
|
||||
&types.FeeAllowanceGrant{},
|
||||
&s.addedGrant,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
s.Run(tc.name, func() {
|
||||
cmd := cli.GetCmdQueryFeeGrant()
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
|
||||
|
||||
if tc.expectErr {
|
||||
s.Require().Error(err)
|
||||
s.Require().Contains(err.Error(), tc.expectErrMsg)
|
||||
} else {
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
|
||||
s.Require().Equal(tc.respType.Grantee, tc.respType.Grantee)
|
||||
s.Require().Equal(tc.respType.Granter, tc.respType.Granter)
|
||||
s.Require().Equal(
|
||||
tc.respType.GetFeeGrant().(*types.BasicFeeAllowance).SpendLimit,
|
||||
tc.resp.GetFeeGrant().(*types.BasicFeeAllowance).SpendLimit,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestCmdGetFeeGrants() {
|
||||
val := s.network.Validators[0]
|
||||
grantee := s.addedGrantee
|
||||
clientCtx := val.ClientCtx
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectErr bool
|
||||
resp *types.QueryFeeAllowancesResponse
|
||||
expectLength int
|
||||
}{
|
||||
{
|
||||
"wrong grantee",
|
||||
[]string{
|
||||
"wrong_grantee",
|
||||
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
|
||||
},
|
||||
true, nil, 0,
|
||||
},
|
||||
{
|
||||
"non existed grantee",
|
||||
[]string{
|
||||
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
|
||||
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
|
||||
},
|
||||
false, &types.QueryFeeAllowancesResponse{}, 0,
|
||||
},
|
||||
{
|
||||
"valid req",
|
||||
[]string{
|
||||
grantee.String(),
|
||||
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
|
||||
},
|
||||
false, &types.QueryFeeAllowancesResponse{}, 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
s.Run(tc.name, func() {
|
||||
cmd := cli.GetCmdQueryFeeGrants()
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
|
||||
|
||||
if tc.expectErr {
|
||||
s.Require().Error(err)
|
||||
} else {
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.resp), out.String())
|
||||
s.Require().Len(tc.resp.FeeAllowances, tc.expectLength)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestNewCmdFeeGrant() {
|
||||
val := s.network.Validators[0]
|
||||
granter := val.Address
|
||||
alreadyExistedGrantee := s.addedGrantee
|
||||
clientCtx := val.ClientCtx
|
||||
|
||||
commonFlags := []string{
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectErr bool
|
||||
respType proto.Message
|
||||
expectedCode uint32
|
||||
}{
|
||||
{
|
||||
"wrong granter address",
|
||||
append(
|
||||
[]string{
|
||||
"wrong_granter",
|
||||
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
true, nil, 0,
|
||||
},
|
||||
{
|
||||
"wrong grantee address",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"wrong_grantee",
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
true, nil, 0,
|
||||
},
|
||||
{
|
||||
"valid basic fee grant",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
false, &sdk.TxResponse{}, 0,
|
||||
},
|
||||
{
|
||||
"valid basic fee grant without spend limit",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos17h5lzptx3ghvsuhk7wx4c4hnl7rsswxjer97em",
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
false, &sdk.TxResponse{}, 0,
|
||||
},
|
||||
{
|
||||
"valid basic fee grant without expiration",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos16dlc38dcqt0uralyd8hksxyrny6kaeqfjvjwp5",
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
false, &sdk.TxResponse{}, 0,
|
||||
},
|
||||
{
|
||||
"valid basic fee grant without spend-limit and expiration",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos1ku40qup9vwag4wtf8cls9mkszxfthaklxkp3c8",
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
false, &sdk.TxResponse{}, 0,
|
||||
},
|
||||
{
|
||||
"try to add existed grant",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
alreadyExistedGrantee.String(),
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
false, &sdk.TxResponse{}, 18,
|
||||
},
|
||||
{
|
||||
"invalid number of args(periodic fee grant)",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
|
||||
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
fmt.Sprintf("--%s=%d", cli.FlagExpiration, 10*60*60),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
true, nil, 0,
|
||||
},
|
||||
{
|
||||
"period mentioned and period limit omitted, invalid periodic grant",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
|
||||
fmt.Sprintf("--%s=%d", cli.FlagPeriod, 10*60*60),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
fmt.Sprintf("--%s=%d", cli.FlagExpiration, 60*60),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
true, nil, 0,
|
||||
},
|
||||
{
|
||||
"period cannot be greater than the actual expiration(periodic fee grant)",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
|
||||
fmt.Sprintf("--%s=%d", cli.FlagPeriod, 10*60*60),
|
||||
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
fmt.Sprintf("--%s=%d", cli.FlagExpiration, 60*60),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
true, nil, 0,
|
||||
},
|
||||
{
|
||||
"valid periodic fee grant",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos1w55kgcf3ltaqdy4ww49nge3klxmrdavrr6frmp",
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
|
||||
fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60),
|
||||
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
fmt.Sprintf("--%s=%d", cli.FlagExpiration, 10*60*60),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
false, &sdk.TxResponse{}, 0,
|
||||
},
|
||||
{
|
||||
"valid periodic fee grant without spend-limit",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos1vevyks8pthkscvgazc97qyfjt40m6g9xe85ry8",
|
||||
fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60),
|
||||
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
fmt.Sprintf("--%s=%d", cli.FlagExpiration, 10*60*60),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
false, &sdk.TxResponse{}, 0,
|
||||
},
|
||||
{
|
||||
"valid periodic fee grant without expiration",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos14cm33pvnrv2497tyt8sp9yavhmw83nwej3m0e8",
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
|
||||
fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60),
|
||||
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
false, &sdk.TxResponse{}, 0,
|
||||
},
|
||||
{
|
||||
"valid periodic fee grant without spend-limit and expiration",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos12nyk4pcf4arshznkpz882e4l4ts0lt0ap8ce54",
|
||||
fmt.Sprintf("--%s=%d", cli.FlagPeriod, 60*60),
|
||||
fmt.Sprintf("--%s=%s", cli.FlagPeriodLimit, "10stake"),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
false, &sdk.TxResponse{}, 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
s.Run(tc.name, func() {
|
||||
cmd := cli.NewCmdFeeGrant()
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
|
||||
|
||||
if tc.expectErr {
|
||||
s.Require().Error(err)
|
||||
} else {
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
|
||||
|
||||
txResp := tc.respType.(*sdk.TxResponse)
|
||||
s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestNewCmdRevokeFeegrant() {
|
||||
val := s.network.Validators[0]
|
||||
granter := s.addedGranter
|
||||
grantee := s.addedGrantee
|
||||
clientCtx := val.ClientCtx
|
||||
|
||||
commonFlags := []string{
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expectErr bool
|
||||
respType proto.Message
|
||||
expectedCode uint32
|
||||
}{
|
||||
{
|
||||
"invalid grantee",
|
||||
append(
|
||||
[]string{
|
||||
"wrong_granter",
|
||||
grantee.String(),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
true,
|
||||
nil,
|
||||
0,
|
||||
},
|
||||
{
|
||||
"invalid grantee",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"wrong_grantee",
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
true,
|
||||
nil,
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Non existed grant",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
"cosmos1aeuqja06474dfrj7uqsvukm6rael982kk89mqr",
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
false,
|
||||
&sdk.TxResponse{},
|
||||
4,
|
||||
},
|
||||
{
|
||||
"Valid revoke",
|
||||
append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
grantee.String(),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
false,
|
||||
&sdk.TxResponse{},
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
s.Run(tc.name, func() {
|
||||
cmd := cli.NewCmdRevokeFeegrant()
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
|
||||
|
||||
if tc.expectErr {
|
||||
s.Require().Error(err)
|
||||
} else {
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
|
||||
|
||||
txResp := tc.respType.(*sdk.TxResponse)
|
||||
s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestTxWithFeeGrant() {
|
||||
val := s.network.Validators[0]
|
||||
clientCtx := val.ClientCtx
|
||||
granter := val.Address
|
||||
|
||||
// creating an account manually (This account won't be exist in state)
|
||||
info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
|
||||
s.Require().NoError(err)
|
||||
grantee := sdk.AccAddress(info.GetPubKey().Address())
|
||||
|
||||
commonFlags := []string{
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
|
||||
}
|
||||
|
||||
fee := sdk.NewCoin("stake", sdk.NewInt(100))
|
||||
duration := 365 * 24 * 60 * 60
|
||||
|
||||
args := append(
|
||||
[]string{
|
||||
granter.String(),
|
||||
grantee.String(),
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
|
||||
fmt.Sprintf("--%s=%v", cli.FlagExpiration, duration),
|
||||
},
|
||||
commonFlags...,
|
||||
)
|
||||
|
||||
cmd := cli.NewCmdFeeGrant()
|
||||
|
||||
_, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.network.WaitForHeight(1)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// granted fee allowance for an account which is not in state and creating
|
||||
// any tx with it by using --fee-account shouldn't fail
|
||||
out, err := govtestutil.MsgSubmitProposal(val.ClientCtx, grantee.String(),
|
||||
"Text Proposal", "No desc", govtypes.ProposalTypeText,
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFeeAccount, granter.String()),
|
||||
)
|
||||
|
||||
s.Require().NoError(err)
|
||||
var resp sdk.TxResponse
|
||||
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &resp), out.String())
|
||||
s.Require().Equal(uint32(0), resp.Code)
|
||||
}
|
||||
|
||||
func TestIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// GetQueryCmd returns the cli query commands for this module
|
||||
func GetQueryCmd() *cobra.Command {
|
||||
feegrantQueryCmd := &cobra.Command{
|
||||
Use: types.ModuleName,
|
||||
Short: "Querying commands for the feegrant module",
|
||||
DisableFlagParsing: true,
|
||||
SuggestionsMinimumDistance: 2,
|
||||
RunE: client.ValidateCmd,
|
||||
}
|
||||
|
||||
feegrantQueryCmd.AddCommand(
|
||||
GetCmdQueryFeeGrant(),
|
||||
GetCmdQueryFeeGrants(),
|
||||
)
|
||||
|
||||
return feegrantQueryCmd
|
||||
}
|
||||
|
||||
// GetCmdQueryFeeGrant returns cmd to query for a grant between granter and grantee.
|
||||
func GetCmdQueryFeeGrant() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "grant [granter] [grantee]",
|
||||
Args: cobra.ExactArgs(2),
|
||||
Short: "Query details of a single grant",
|
||||
Long: strings.TrimSpace(
|
||||
fmt.Sprintf(`Query details for a grant.
|
||||
You can find the fee-grant of a granter and grantee.
|
||||
|
||||
Example:
|
||||
$ %s query feegrant grant [granter] [grantee]
|
||||
`, version.AppName),
|
||||
),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
queryClient := types.NewQueryClient(clientCtx)
|
||||
|
||||
granterAddr, err := sdk.AccAddressFromBech32(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
granteeAddr, err := sdk.AccAddressFromBech32(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := queryClient.FeeAllowance(
|
||||
cmd.Context(),
|
||||
&types.QueryFeeAllowanceRequest{
|
||||
Granter: granterAddr.String(),
|
||||
Grantee: granteeAddr.String(),
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return clientCtx.PrintProto(res.FeeAllowance)
|
||||
},
|
||||
}
|
||||
|
||||
flags.AddQueryFlagsToCmd(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// GetCmdQueryFeeGrants returns cmd to query for all grants for a grantee.
|
||||
func GetCmdQueryFeeGrants() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "grants [grantee]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "Query all grants of a grantee",
|
||||
Long: strings.TrimSpace(
|
||||
fmt.Sprintf(`Queries all the grants for a grantee address.
|
||||
|
||||
Example:
|
||||
$ %s query feegrant grants [grantee]
|
||||
`, version.AppName),
|
||||
),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
queryClient := types.NewQueryClient(clientCtx)
|
||||
|
||||
granteeAddr, err := sdk.AccAddressFromBech32(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pageReq, err := client.ReadPageRequest(cmd.Flags())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := queryClient.FeeAllowances(
|
||||
cmd.Context(),
|
||||
&types.QueryFeeAllowancesRequest{
|
||||
Grantee: granteeAddr.String(),
|
||||
Pagination: pageReq,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return clientCtx.PrintProto(res)
|
||||
},
|
||||
}
|
||||
|
||||
flags.AddQueryFlagsToCmd(cmd)
|
||||
flags.AddPaginationFlagsToCmd(cmd, "grants")
|
||||
|
||||
return cmd
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/msgservice"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
)
|
||||
|
||||
// flag for feegrant module
|
||||
const (
|
||||
FlagExpiration = "expiration"
|
||||
FlagPeriod = "period"
|
||||
FlagPeriodLimit = "period-limit"
|
||||
FlagSpendLimit = "spend-limit"
|
||||
)
|
||||
|
||||
// GetTxCmd returns the transaction commands for this module
|
||||
func GetTxCmd() *cobra.Command {
|
||||
feegrantTxCmd := &cobra.Command{
|
||||
Use: types.ModuleName,
|
||||
Short: "Feegrant transactions subcommands",
|
||||
Long: "Grant and revoke fee allowance for a grantee by a granter",
|
||||
DisableFlagParsing: true,
|
||||
SuggestionsMinimumDistance: 2,
|
||||
RunE: client.ValidateCmd,
|
||||
}
|
||||
|
||||
feegrantTxCmd.AddCommand(
|
||||
NewCmdFeeGrant(),
|
||||
NewCmdRevokeFeegrant(),
|
||||
)
|
||||
|
||||
return feegrantTxCmd
|
||||
}
|
||||
|
||||
// NewCmdFeeGrant returns a CLI command handler for creating a MsgGrantFeeAllowance transaction.
|
||||
func NewCmdFeeGrant() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "grant [granter] [grantee]",
|
||||
Short: "Grant Fee allowance to an address",
|
||||
Long: strings.TrimSpace(
|
||||
fmt.Sprintf(
|
||||
`Grant authorization to pay fees from your address. Note, the'--from' flag is
|
||||
ignored as it is implied from [granter].
|
||||
|
||||
Examples:
|
||||
%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --expiration 36000 or
|
||||
%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --period 3600 --period-limit 10stake --expiration 36000
|
||||
`, version.AppName, types.ModuleName, version.AppName, types.ModuleName,
|
||||
),
|
||||
),
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
_, err := sdk.AccAddressFromBech32(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Flags().Set(flags.FlagFrom, args[0])
|
||||
clientCtx, err := client.GetClientTxContext(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
grantee, err := sdk.AccAddressFromBech32(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
granter := clientCtx.GetFromAddress()
|
||||
sl, err := cmd.Flags().GetString(FlagSpendLimit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if `FlagSpendLimit` isn't set, limit will be nil
|
||||
limit, err := sdk.ParseCoinsNormalized(sl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exp, err := cmd.Flags().GetInt64(FlagExpiration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
basic := types.BasicFeeAllowance{
|
||||
SpendLimit: limit,
|
||||
}
|
||||
|
||||
if exp != 0 {
|
||||
expDuration := time.Duration(exp) * time.Second
|
||||
basic.Expiration = types.ExpiresAtTime(time.Now().Add(expDuration))
|
||||
}
|
||||
|
||||
var grant types.FeeAllowanceI
|
||||
grant = &basic
|
||||
|
||||
periodClock, err := cmd.Flags().GetInt64(FlagPeriod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
periodLimitVal, err := cmd.Flags().GetString(FlagPeriodLimit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check any of period or periodLimit flags set, If set consider it as periodic fee allowance.
|
||||
if periodClock > 0 || periodLimitVal != "" {
|
||||
periodLimit, err := sdk.ParseCoinsNormalized(periodLimitVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if periodClock > 0 && periodLimit != nil {
|
||||
if exp > 0 && periodClock > exp {
|
||||
return fmt.Errorf("period(%d) cannot be greater than the expiration(%d)", periodClock, exp)
|
||||
}
|
||||
|
||||
periodic := types.PeriodicFeeAllowance{
|
||||
Basic: basic,
|
||||
Period: types.ClockDuration(time.Duration(periodClock) * time.Second),
|
||||
PeriodReset: types.ExpiresAtTime(time.Now().Add(time.Duration(periodClock) * time.Second)),
|
||||
PeriodSpendLimit: periodLimit,
|
||||
PeriodCanSpend: periodLimit,
|
||||
}
|
||||
|
||||
grant = &periodic
|
||||
|
||||
} else {
|
||||
return fmt.Errorf("invalid number of args %d", len(args))
|
||||
}
|
||||
}
|
||||
|
||||
msg, err := types.NewMsgGrantFeeAllowance(grant, granter, grantee)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
svcMsgClientConn := &msgservice.ServiceMsgClientConn{}
|
||||
feeGrantMsgClient := types.NewMsgClient(svcMsgClientConn)
|
||||
_, err = feeGrantMsgClient.GrantFeeAllowance(cmd.Context(), msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...)
|
||||
},
|
||||
}
|
||||
|
||||
flags.AddTxFlagsToCmd(cmd)
|
||||
cmd.Flags().Int64(FlagExpiration, 0, "The second unit of time duration which the grant is active for the user")
|
||||
cmd.Flags().String(FlagSpendLimit, "", "Spend limit specifies the max limit can be used, if not mentioned there is no limit")
|
||||
cmd.Flags().Int64(FlagPeriod, 0, "period specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset")
|
||||
cmd.Flags().String(FlagPeriodLimit, "", "// period limit specifies the maximum number of coins that can be spent in the period")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// NewCmdRevokeFeegrant returns a CLI command handler for creating a MsgRevokeFeeAllowance transaction.
|
||||
func NewCmdRevokeFeegrant() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "revoke [granter_address] [grantee_address]",
|
||||
Short: "revoke fee-grant",
|
||||
Long: strings.TrimSpace(
|
||||
fmt.Sprintf(`revoke fee grant from a granter to a grantee. Note, the'--from' flag is
|
||||
ignored as it is implied from [granter_address].
|
||||
|
||||
Example:
|
||||
$ %s tx %s revoke cosmos1skj.. cosmos1skj..
|
||||
`, version.AppName, types.ModuleName),
|
||||
),
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cmd.Flags().Set(flags.FlagFrom, args[0])
|
||||
clientCtx, err := client.GetClientTxContext(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
grantee, err := sdk.AccAddressFromBech32(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := types.NewMsgRevokeFeeAllowance(clientCtx.GetFromAddress(), grantee)
|
||||
svcMsgClientConn := &msgservice.ServiceMsgClientConn{}
|
||||
feeGrantMsgClient := types.NewMsgClient(svcMsgClientConn)
|
||||
_, err = feeGrantMsgClient.RevokeFeeAllowance(cmd.Context(), &msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), svcMsgClientConn.GetMsgs()...)
|
||||
},
|
||||
}
|
||||
|
||||
flags.AddTxFlagsToCmd(cmd)
|
||||
return cmd
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
package rest_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/client/cli"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
cfg network.Config
|
||||
network *network.Network
|
||||
grantee sdk.AccAddress
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
val := s.network.Validators[0]
|
||||
// Create new account in the keyring.
|
||||
info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
|
||||
s.Require().NoError(err)
|
||||
newAddr := sdk.AccAddress(info.GetPubKey().Address())
|
||||
|
||||
// Send some funds to the new account.
|
||||
_, err = banktestutil.MsgSendExec(
|
||||
val.ClientCtx,
|
||||
val.Address,
|
||||
newAddr,
|
||||
sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(200))), 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()),
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.grantee = newAddr
|
||||
_, err = s.network.WaitForHeight(1)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TearDownSuite() {
|
||||
s.T().Log("tearing down integration test suite")
|
||||
s.network.Cleanup()
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestQueryFeeAllowance() {
|
||||
val := s.network.Validators[0]
|
||||
baseURL := val.APIAddress
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
expectErr bool
|
||||
errorMsg string
|
||||
preRun func()
|
||||
postRun func(_ types.QueryFeeAllowanceResponse)
|
||||
}{
|
||||
{
|
||||
"fail: invalid granter",
|
||||
fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, "invalid_granter", s.grantee.String()),
|
||||
true,
|
||||
"decoding bech32 failed: invalid index of 1: invalid request",
|
||||
func() {},
|
||||
func(types.QueryFeeAllowanceResponse) {},
|
||||
},
|
||||
{
|
||||
"fail: invalid grantee",
|
||||
fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, val.Address.String(), "invalid_grantee"),
|
||||
true,
|
||||
"decoding bech32 failed: invalid index of 1: invalid request",
|
||||
func() {},
|
||||
func(types.QueryFeeAllowanceResponse) {},
|
||||
},
|
||||
{
|
||||
"fail: no grants",
|
||||
fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, val.Address.String(), s.grantee.String()),
|
||||
true,
|
||||
"no fee allowance found",
|
||||
func() {},
|
||||
func(types.QueryFeeAllowanceResponse) {},
|
||||
},
|
||||
{
|
||||
"valid query: expect single grant",
|
||||
fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, val.Address.String(), s.grantee.String()),
|
||||
false,
|
||||
"",
|
||||
func() {
|
||||
execFeeAllowance(val, s)
|
||||
},
|
||||
func(allowance types.QueryFeeAllowanceResponse) {
|
||||
s.Require().Equal(allowance.FeeAllowance.Granter, val.Address.String())
|
||||
s.Require().Equal(allowance.FeeAllowance.Grantee, s.grantee.String())
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
tc.preRun()
|
||||
resp, _ := rest.GetRequest(tc.url)
|
||||
if tc.expectErr {
|
||||
s.Require().Contains(string(resp), tc.errorMsg)
|
||||
} else {
|
||||
var allowance types.QueryFeeAllowanceResponse
|
||||
err := val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &allowance)
|
||||
s.Require().NoError(err)
|
||||
tc.postRun(allowance)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TestQueryGranteeAllowances() {
|
||||
val := s.network.Validators[0]
|
||||
baseURL := val.APIAddress
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
expectErr bool
|
||||
errorMsg string
|
||||
preRun func()
|
||||
postRun func(_ types.QueryFeeAllowancesResponse)
|
||||
}{
|
||||
{
|
||||
"fail: invalid grantee",
|
||||
fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowances/%s", baseURL, "invalid_grantee"),
|
||||
true,
|
||||
"decoding bech32 failed: invalid index of 1: invalid request",
|
||||
func() {},
|
||||
func(types.QueryFeeAllowancesResponse) {},
|
||||
},
|
||||
{
|
||||
"success: no grants",
|
||||
fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowances/%s?pagination.offset=1", baseURL, s.grantee.String()),
|
||||
false,
|
||||
"",
|
||||
func() {},
|
||||
func(allowances types.QueryFeeAllowancesResponse) {
|
||||
s.Require().Equal(len(allowances.FeeAllowances), 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
"valid query: expect single grant",
|
||||
fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowances/%s", baseURL, s.grantee.String()),
|
||||
false,
|
||||
"",
|
||||
func() {
|
||||
execFeeAllowance(val, s)
|
||||
},
|
||||
func(allowances types.QueryFeeAllowancesResponse) {
|
||||
s.Require().Equal(len(allowances.FeeAllowances), 1)
|
||||
s.Require().Equal(allowances.FeeAllowances[0].Granter, val.Address.String())
|
||||
s.Require().Equal(allowances.FeeAllowances[0].Grantee, s.grantee.String())
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
s.Run(tc.name, func() {
|
||||
tc.preRun()
|
||||
resp, _ := rest.GetRequest(tc.url)
|
||||
if tc.expectErr {
|
||||
s.Require().Contains(string(resp), tc.errorMsg)
|
||||
} else {
|
||||
var allowance types.QueryFeeAllowancesResponse
|
||||
err := val.ClientCtx.JSONMarshaler.UnmarshalJSON(resp, &allowance)
|
||||
s.Require().NoError(err)
|
||||
tc.postRun(allowance)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func execFeeAllowance(val *network.Validator, s *IntegrationTestSuite) {
|
||||
fee := sdk.NewCoin("steak", sdk.NewInt(100))
|
||||
duration := 365 * 24 * 60 * 60
|
||||
args := []string{
|
||||
val.Address.String(),
|
||||
s.grantee.String(),
|
||||
fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, fee.String()),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
|
||||
fmt.Sprintf("--%s=%v", cli.FlagExpiration, duration),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
|
||||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
|
||||
}
|
||||
|
||||
cmd := cli.NewCmdFeeGrant()
|
||||
_, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func TestIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
Package feegrant provides functionality for authorizing the payment of transaction
|
||||
fees from one account (key) to another account (key).
|
||||
|
||||
Effectively, this allows for a user to pay fees using the balance of an account
|
||||
different from their own. Example use cases would be allowing a key on a device to
|
||||
pay for fees using a master wallet, or a third party service allowing users to
|
||||
pay for transactions without ever really holding their own tokens. This package
|
||||
provides ways for specifying fee allowances such that authorizing fee payment to
|
||||
another account can be done with clear and safe restrictions.
|
||||
|
||||
A user would authorize granting fee payment to another user using
|
||||
MsgDelegateFeeAllowance and revoke that delegation using MsgRevokeFeeAllowance.
|
||||
In both cases, Granter is the one who is authorizing fee payment and Grantee is
|
||||
the one who is receiving the fee payment authorization. So grantee would correspond
|
||||
to the one who is signing a transaction and the granter would be the address that
|
||||
pays the fees.
|
||||
|
||||
The fee allowance that a grantee receives is specified by an implementation of
|
||||
the FeeAllowance interface. Two FeeAllowance implementations are provided in
|
||||
this package: BasicFeeAllowance and PeriodicFeeAllowance.
|
||||
|
||||
In order to integrate this into an application, we must use the DeductGrantedFeeDecorator
|
||||
ante handler from this package instead of the default DeductFeeDecorator from x/auth.
|
||||
|
||||
To allow handling txs from empty accounts (with fees paid from an existing account),
|
||||
we have to re-order the decorators as well.
|
||||
*/
|
||||
package feegrant
|
|
@ -0,0 +1,61 @@
|
|||
package feegrant
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
)
|
||||
|
||||
// GenesisState contains a set of fee allowances, persisted from the store
|
||||
type GenesisState []types.FeeAllowanceGrant
|
||||
|
||||
// ValidateBasic ensures all grants in the genesis state are valid
|
||||
func (g GenesisState) ValidateBasic() error {
|
||||
for _, f := range g {
|
||||
err := f.GetFeeGrant().ValidateBasic()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitGenesis will initialize the keeper from a *previously validated* GenesisState
|
||||
func InitGenesis(ctx sdk.Context, k keeper.Keeper, data *types.GenesisState) {
|
||||
for _, f := range data.FeeAllowances {
|
||||
granter, err := sdk.AccAddressFromBech32(f.Granter)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
grantee, err := sdk.AccAddressFromBech32(f.Grantee)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = k.GrantFeeAllowance(ctx, granter, grantee, f.GetFeeGrant())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ExportGenesis will dump the contents of the keeper into a serializable GenesisState
|
||||
//
|
||||
// All expiration heights will be thrown off if we dump state and start at a new
|
||||
// chain at height 0. Thus, we allow the Allowances to "prepare themselves"
|
||||
// for export, like if they have expiry at 5000 and current is 4000, they export with
|
||||
// expiry of 1000. Every FeeAllowance has a method `PrepareForExport` that allows
|
||||
// them to perform any changes needed prior to export.
|
||||
func ExportGenesis(ctx sdk.Context, k keeper.Keeper) (*types.GenesisState, error) {
|
||||
time, height := ctx.BlockTime(), ctx.BlockHeight()
|
||||
var grants []types.FeeAllowanceGrant
|
||||
|
||||
err := k.IterateAllFeeAllowances(ctx, func(grant types.FeeAllowanceGrant) bool {
|
||||
grants = append(grants, grant.PrepareForExport(time, height))
|
||||
return false
|
||||
})
|
||||
|
||||
return &types.GenesisState{
|
||||
FeeAllowances: grants,
|
||||
}, err
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package feegrant_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
feegrant "github.com/cosmos/cosmos-sdk/x/feegrant"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
type GenesisTestSuite struct {
|
||||
suite.Suite
|
||||
ctx sdk.Context
|
||||
keeper keeper.Keeper
|
||||
}
|
||||
|
||||
func (suite *GenesisTestSuite) SetupTest() {
|
||||
checkTx := false
|
||||
app := simapp.Setup(checkTx)
|
||||
suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1})
|
||||
suite.keeper = app.FeeGrantKeeper
|
||||
}
|
||||
|
||||
var (
|
||||
granteePub = secp256k1.GenPrivKey().PubKey()
|
||||
granterPub = secp256k1.GenPrivKey().PubKey()
|
||||
granteeAddr = sdk.AccAddress(granteePub.Address())
|
||||
granterAddr = sdk.AccAddress(granterPub.Address())
|
||||
)
|
||||
|
||||
func (suite *GenesisTestSuite) TestImportExportGenesis() {
|
||||
coins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1_000)))
|
||||
now := suite.ctx.BlockHeader().Time
|
||||
|
||||
allowance := &types.BasicFeeAllowance{SpendLimit: coins, Expiration: types.ExpiresAtTime(now.AddDate(1, 0, 0))}
|
||||
err := suite.keeper.GrantFeeAllowance(suite.ctx, granterAddr, granteeAddr, allowance)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
genesis, err := feegrant.ExportGenesis(suite.ctx, suite.keeper)
|
||||
suite.Require().NoError(err)
|
||||
// Clear keeper
|
||||
suite.keeper.RevokeFeeAllowance(suite.ctx, granterAddr, granteeAddr)
|
||||
feegrant.InitGenesis(suite.ctx, suite.keeper, genesis)
|
||||
newGenesis, err := feegrant.ExportGenesis(suite.ctx, suite.keeper)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(genesis, newGenesis)
|
||||
}
|
||||
|
||||
func TestGenesisTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(GenesisTestSuite))
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/query"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
)
|
||||
|
||||
var _ types.QueryServer = Keeper{}
|
||||
|
||||
// FeeAllowance returns fee granted to the grantee by the granter.
|
||||
func (q Keeper) FeeAllowance(c context.Context, req *types.QueryFeeAllowanceRequest) (*types.QueryFeeAllowanceResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid request")
|
||||
}
|
||||
|
||||
granterAddr, err := sdk.AccAddressFromBech32(req.Granter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
granteeAddr, err := sdk.AccAddressFromBech32(req.Grantee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
feeAllowance := q.GetFeeAllowance(ctx, granterAddr, granteeAddr)
|
||||
if feeAllowance == nil {
|
||||
return nil, status.Errorf(codes.NotFound, "no fee allowance found")
|
||||
}
|
||||
|
||||
msg, ok := feeAllowance.(proto.Message)
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "can't proto marshal %T", msg)
|
||||
}
|
||||
|
||||
feeAllowanceAny, err := codectypes.NewAnyWithValue(msg)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &types.QueryFeeAllowanceResponse{
|
||||
FeeAllowance: &types.FeeAllowanceGrant{
|
||||
Granter: granterAddr.String(),
|
||||
Grantee: granteeAddr.String(),
|
||||
Allowance: feeAllowanceAny,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (q Keeper) FeeAllowances(c context.Context, req *types.QueryFeeAllowancesRequest) (*types.QueryFeeAllowancesResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid request")
|
||||
}
|
||||
|
||||
granteeAddr, err := sdk.AccAddressFromBech32(req.Grantee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
var grants []*types.FeeAllowanceGrant
|
||||
|
||||
store := ctx.KVStore(q.storeKey)
|
||||
grantsStore := prefix.NewStore(store, types.FeeAllowancePrefixByGrantee(granteeAddr))
|
||||
|
||||
pageRes, err := query.Paginate(grantsStore, req.Pagination, func(key []byte, value []byte) error {
|
||||
var grant types.FeeAllowanceGrant
|
||||
|
||||
if err := q.cdc.UnmarshalBinaryBare(value, &grant); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
grants = append(grants, &grant)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &types.QueryFeeAllowancesResponse{FeeAllowances: grants, Pagination: pageRes}, nil
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
)
|
||||
|
||||
// Keeper manages state of all fee grants, as well as calculating approval.
|
||||
// It must have a codec with all available allowances registered.
|
||||
type Keeper struct {
|
||||
cdc codec.BinaryMarshaler
|
||||
storeKey sdk.StoreKey
|
||||
authKeeper types.AccountKeeper
|
||||
}
|
||||
|
||||
// NewKeeper creates a fee grant Keeper
|
||||
func NewKeeper(cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, ak types.AccountKeeper) Keeper {
|
||||
return Keeper{
|
||||
cdc: cdc,
|
||||
storeKey: storeKey,
|
||||
authKeeper: ak,
|
||||
}
|
||||
}
|
||||
|
||||
// Logger returns a module-specific logger.
|
||||
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
|
||||
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
|
||||
}
|
||||
|
||||
// GrantFeeAllowance creates a new grant
|
||||
func (k Keeper) GrantFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress, feeAllowance types.FeeAllowanceI) error {
|
||||
|
||||
// create the account if it is not in account state
|
||||
granteeAcc := k.authKeeper.GetAccount(ctx, grantee)
|
||||
if granteeAcc == nil {
|
||||
granteeAcc = k.authKeeper.NewAccountWithAddress(ctx, grantee)
|
||||
k.authKeeper.SetAccount(ctx, granteeAcc)
|
||||
}
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
key := types.FeeAllowanceKey(granter, grantee)
|
||||
grant, err := types.NewFeeAllowanceGrant(granter, grantee, feeAllowance)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bz, err := k.cdc.MarshalBinaryBare(&grant)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
store.Set(key, bz)
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeSetFeeGrant,
|
||||
sdk.NewAttribute(types.AttributeKeyGranter, grant.Granter),
|
||||
sdk.NewAttribute(types.AttributeKeyGrantee, grant.Grantee),
|
||||
),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeFeeAllowance removes an existing grant
|
||||
func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) error {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
key := types.FeeAllowanceKey(granter, grantee)
|
||||
_, found := k.GetFeeGrant(ctx, granter, grantee)
|
||||
if !found {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "fee-grant not found")
|
||||
}
|
||||
|
||||
store.Delete(key)
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeRevokeFeeGrant,
|
||||
sdk.NewAttribute(types.AttributeKeyGranter, granter.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyGrantee, grantee.String()),
|
||||
),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFeeAllowance returns the allowance between the granter and grantee.
|
||||
// If there is none, it returns nil, nil.
|
||||
// Returns an error on parsing issues
|
||||
func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) types.FeeAllowanceI {
|
||||
grant, found := k.GetFeeGrant(ctx, granter, grantee)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
return grant.GetFeeGrant()
|
||||
}
|
||||
|
||||
// GetFeeGrant returns entire grant between both accounts
|
||||
func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) (types.FeeAllowanceGrant, bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
key := types.FeeAllowanceKey(granter, grantee)
|
||||
bz := store.Get(key)
|
||||
if len(bz) == 0 {
|
||||
return types.FeeAllowanceGrant{}, false
|
||||
}
|
||||
|
||||
var feegrant types.FeeAllowanceGrant
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &feegrant)
|
||||
|
||||
return feegrant, true
|
||||
}
|
||||
|
||||
// IterateAllGranteeFeeAllowances iterates over all the grants from anyone to the given grantee.
|
||||
// Callback to get all data, returns true to stop, false to keep reading
|
||||
func (k Keeper) IterateAllGranteeFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress, cb func(types.FeeAllowanceGrant) bool) error {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
prefix := types.FeeAllowancePrefixByGrantee(grantee)
|
||||
iter := sdk.KVStorePrefixIterator(store, prefix)
|
||||
defer iter.Close()
|
||||
|
||||
stop := false
|
||||
for ; iter.Valid() && !stop; iter.Next() {
|
||||
bz := iter.Value()
|
||||
|
||||
var feeGrant types.FeeAllowanceGrant
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &feeGrant)
|
||||
|
||||
stop = cb(feeGrant)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IterateAllFeeAllowances iterates over all the grants in the store.
|
||||
// Callback to get all data, returns true to stop, false to keep reading
|
||||
// Calling this without pagination is very expensive and only designed for export genesis
|
||||
func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(types.FeeAllowanceGrant) bool) error {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iter := sdk.KVStorePrefixIterator(store, types.FeeAllowanceKeyPrefix)
|
||||
defer iter.Close()
|
||||
|
||||
stop := false
|
||||
for ; iter.Valid() && !stop; iter.Next() {
|
||||
bz := iter.Value()
|
||||
var feeGrant types.FeeAllowanceGrant
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &feeGrant)
|
||||
|
||||
stop = cb(feeGrant)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UseGrantedFees will try to pay the given fee from the granter's account as requested by the grantee
|
||||
func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) error {
|
||||
grant, found := k.GetFeeGrant(ctx, granter, grantee)
|
||||
if !found || grant.GetFeeGrant() == nil {
|
||||
return sdkerrors.Wrapf(types.ErrNoAllowance, "grant missing")
|
||||
}
|
||||
|
||||
remove, err := grant.GetFeeGrant().Accept(fee, ctx.BlockTime(), ctx.BlockHeight())
|
||||
if err == nil {
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeUseFeeGrant,
|
||||
sdk.NewAttribute(types.AttributeKeyGranter, granter.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyGrantee, grantee.String()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if remove {
|
||||
k.RevokeFeeAllowance(ctx, granter, grantee)
|
||||
// note this returns nil if err == nil
|
||||
return sdkerrors.Wrap(err, "removed grant")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(err, "invalid grant")
|
||||
}
|
||||
|
||||
// if we accepted, store the updated state of the allowance
|
||||
return k.GrantFeeAllowance(ctx, granter, grantee, grant.GetFeeGrant())
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue