Merge PR #4788: Query genesis transactions
This commit is contained in:
parent
fd92504f84
commit
2e3c52bae1
|
@ -0,0 +1 @@
|
||||||
|
# 3867 Allow querying for genesis transaction when height query param is set to zero.
|
|
@ -142,7 +142,7 @@ func NewSimApp(
|
||||||
// add keepers
|
// add keepers
|
||||||
app.accountKeeper = auth.NewAccountKeeper(app.cdc, keys[auth.StoreKey], authSubspace, auth.ProtoBaseAccount)
|
app.accountKeeper = auth.NewAccountKeeper(app.cdc, keys[auth.StoreKey], authSubspace, auth.ProtoBaseAccount)
|
||||||
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace, app.ModuleAccountAddrs())
|
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace, app.ModuleAccountAddrs())
|
||||||
app.supplyKeeper = supply.NewKeeper(app.cdc, keys[supply.StoreKey], app.accountKeeper, app.bankKeeper, supply.DefaultCodespace, maccPerms)
|
app.supplyKeeper = supply.NewKeeper(app.cdc, keys[supply.StoreKey], app.accountKeeper, app.bankKeeper, maccPerms)
|
||||||
stakingKeeper := staking.NewKeeper(app.cdc, keys[staking.StoreKey], tkeys[staking.TStoreKey],
|
stakingKeeper := staking.NewKeeper(app.cdc, keys[staking.StoreKey], tkeys[staking.TStoreKey],
|
||||||
app.supplyKeeper, stakingSubspace, staking.DefaultCodespace)
|
app.supplyKeeper, stakingSubspace, staking.DefaultCodespace)
|
||||||
app.mintKeeper = mint.NewKeeper(app.cdc, keys[mint.StoreKey], mintSubspace, &stakingKeeper, app.supplyKeeper, auth.FeeCollectorName)
|
app.mintKeeper = mint.NewKeeper(app.cdc, keys[mint.StoreKey], mintSubspace, &stakingKeeper, app.supplyKeeper, auth.FeeCollectorName)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package rest
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
@ -12,6 +13,7 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
|
genutilrest "github.com/cosmos/cosmos-sdk/x/genutil/client/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// query accountREST Handler
|
// query accountREST Handler
|
||||||
|
@ -50,16 +52,11 @@ func QueryAccountRequestHandlerFn(storeName string, cliCtx context.CLIContext) h
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryTxsByEventsRequestHandlerFn implements a REST handler that searches for
|
// QueryTxsHandlerFn implements a REST handler that searches for transactions.
|
||||||
// transactions by events.
|
// Genesis transactions are returned if the height parameter is set to zero,
|
||||||
func QueryTxsByEventsRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
// otherwise the transactions are searched for by events.
|
||||||
|
func QueryTxsRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
|
||||||
tags []string
|
|
||||||
txs []sdk.TxResponse
|
|
||||||
page, limit int
|
|
||||||
)
|
|
||||||
|
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rest.WriteErrorResponse(w, http.StatusBadRequest,
|
rest.WriteErrorResponse(w, http.StatusBadRequest,
|
||||||
|
@ -67,6 +64,21 @@ func QueryTxsByEventsRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFun
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the height query param is set to zero, query for genesis transactions
|
||||||
|
heightStr := r.FormValue("height")
|
||||||
|
if heightStr != "" {
|
||||||
|
if height, err := strconv.ParseInt(heightStr, 10, 64); err == nil && height == 0 {
|
||||||
|
genutilrest.QueryGenesisTxs(cliCtx, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
events []string
|
||||||
|
txs []sdk.TxResponse
|
||||||
|
page, limit int
|
||||||
|
)
|
||||||
|
|
||||||
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
|
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -77,13 +89,13 @@ func QueryTxsByEventsRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFun
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tags, page, limit, err = rest.ParseHTTPArgs(r)
|
events, page, limit, err = rest.ParseHTTPArgs(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
searchResult, err := utils.QueryTxsByEvents(cliCtx, tags, page, limit)
|
searchResult, err := utils.QueryTxsByEvents(cliCtx, events, page, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
|
|
|
@ -16,7 +16,7 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string)
|
||||||
// RegisterTxRoutes registers all transaction routes on the provided router.
|
// RegisterTxRoutes registers all transaction routes on the provided router.
|
||||||
func RegisterTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
func RegisterTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
||||||
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cliCtx)).Methods("GET")
|
r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cliCtx)).Methods("GET")
|
||||||
r.HandleFunc("/txs", QueryTxsByEventsRequestHandlerFn(cliCtx)).Methods("GET")
|
r.HandleFunc("/txs", QueryTxsRequestHandlerFn(cliCtx)).Methods("GET")
|
||||||
r.HandleFunc("/txs", BroadcastTxRequest(cliCtx)).Methods("POST")
|
r.HandleFunc("/txs", BroadcastTxRequest(cliCtx)).Methods("POST")
|
||||||
r.HandleFunc("/txs/encode", EncodeTxRequestHandlerFn(cliCtx)).Methods("POST")
|
r.HandleFunc("/txs/encode", EncodeTxRequestHandlerFn(cliCtx)).Methods("POST")
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,7 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initPower int64,
|
||||||
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
||||||
staking.BondedPoolName: {supply.Burner, supply.Staking},
|
staking.BondedPoolName: {supply.Burner, supply.Staking},
|
||||||
}
|
}
|
||||||
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, supply.DefaultCodespace, maccPerms)
|
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms)
|
||||||
|
|
||||||
sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
|
sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
|
||||||
sk.SetParams(ctx, staking.DefaultParams())
|
sk.SetParams(ctx, staking.DefaultParams())
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// QueryGenesisTxs writes the genesis transactions to the response if no error
|
||||||
|
// occurs.
|
||||||
|
func QueryGenesisTxs(cliCtx context.CLIContext, w http.ResponseWriter) {
|
||||||
|
resultGenesis, err := cliCtx.Client.Genesis()
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusInternalServerError,
|
||||||
|
sdk.AppendMsgToErr("could not retrieve genesis from client", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
appState, err := types.GenesisStateFromGenDoc(cliCtx.Codec, *resultGenesis.Genesis)
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusInternalServerError,
|
||||||
|
sdk.AppendMsgToErr("could not decode genesis doc", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
genState := types.GetGenesisStateFromAppState(cliCtx.Codec, appState)
|
||||||
|
genTxs := make([]sdk.Tx, len(genState.GenTxs))
|
||||||
|
for i, tx := range genState.GenTxs {
|
||||||
|
err := cliCtx.Codec.UnmarshalJSON(tx, &genTxs[i])
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusInternalServerError,
|
||||||
|
sdk.AppendMsgToErr("could not decode genesis transaction", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rest.PostProcessResponse(w, cliCtx, genTxs)
|
||||||
|
}
|
|
@ -74,7 +74,7 @@ func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []a
|
||||||
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
||||||
staking.BondedPoolName: {supply.Burner, supply.Staking},
|
staking.BondedPoolName: {supply.Burner, supply.Staking},
|
||||||
}
|
}
|
||||||
supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bk, supply.DefaultCodespace, maccPerms)
|
supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bk, maccPerms)
|
||||||
sk := staking.NewKeeper(mApp.Cdc, keyStaking, tKeyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
|
sk := staking.NewKeeper(mApp.Cdc, keyStaking, tKeyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
|
||||||
|
|
||||||
keeper := NewKeeper(mApp.Cdc, keyGov, pk, pk.Subspace(DefaultParamspace), supplyKeeper, sk, DefaultCodespace, rtr)
|
keeper := NewKeeper(mApp.Cdc, keyGov, pk, pk.Subspace(DefaultParamspace), supplyKeeper, sk, DefaultCodespace, rtr)
|
||||||
|
|
|
@ -74,7 +74,7 @@ func newTestInput(t *testing.T) testInput {
|
||||||
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
||||||
staking.BondedPoolName: {supply.Burner, supply.Staking},
|
staking.BondedPoolName: {supply.Burner, supply.Staking},
|
||||||
}
|
}
|
||||||
supplyKeeper := supply.NewKeeper(types.ModuleCdc, keySupply, accountKeeper, bankKeeper, supply.DefaultCodespace, maccPerms)
|
supplyKeeper := supply.NewKeeper(types.ModuleCdc, keySupply, accountKeeper, bankKeeper, maccPerms)
|
||||||
supplyKeeper.SetSupply(ctx, supply.NewSupply(sdk.Coins{}))
|
supplyKeeper.SetSupply(ctx, supply.NewSupply(sdk.Coins{}))
|
||||||
|
|
||||||
stakingKeeper := staking.NewKeeper(
|
stakingKeeper := staking.NewKeeper(
|
||||||
|
|
|
@ -53,7 +53,7 @@ func getMockApp(t *testing.T) (*mock.App, staking.Keeper, Keeper) {
|
||||||
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
||||||
staking.BondedPoolName: {supply.Burner, supply.Staking},
|
staking.BondedPoolName: {supply.Burner, supply.Staking},
|
||||||
}
|
}
|
||||||
supplyKeeper := supply.NewKeeper(mapp.Cdc, keySupply, mapp.AccountKeeper, bankKeeper, supply.DefaultCodespace, maccPerms)
|
supplyKeeper := supply.NewKeeper(mapp.Cdc, keySupply, mapp.AccountKeeper, bankKeeper, maccPerms)
|
||||||
stakingKeeper := staking.NewKeeper(mapp.Cdc, keyStaking, tkeyStaking, supplyKeeper, mapp.ParamsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
|
stakingKeeper := staking.NewKeeper(mapp.Cdc, keyStaking, tkeyStaking, supplyKeeper, mapp.ParamsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
|
||||||
keeper := NewKeeper(mapp.Cdc, keySlashing, stakingKeeper, mapp.ParamsKeeper.Subspace(DefaultParamspace), DefaultCodespace)
|
keeper := NewKeeper(mapp.Cdc, keySlashing, stakingKeeper, mapp.ParamsKeeper.Subspace(DefaultParamspace), DefaultCodespace)
|
||||||
mapp.Router().AddRoute(staking.RouterKey, staking.NewHandler(stakingKeeper))
|
mapp.Router().AddRoute(staking.RouterKey, staking.NewHandler(stakingKeeper))
|
||||||
|
|
|
@ -99,7 +99,7 @@ func CreateTestInput(t *testing.T, defaults types.Params) (sdk.Context, bank.Kee
|
||||||
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
||||||
staking.BondedPoolName: {supply.Burner, supply.Staking},
|
staking.BondedPoolName: {supply.Burner, supply.Staking},
|
||||||
}
|
}
|
||||||
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bk, supply.DefaultCodespace, maccPerms)
|
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bk, maccPerms)
|
||||||
|
|
||||||
totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, InitTokens.MulRaw(int64(len(Addrs)))))
|
totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, InitTokens.MulRaw(int64(len(Addrs)))))
|
||||||
supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply))
|
supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply))
|
||||||
|
|
|
@ -41,7 +41,7 @@ func getMockApp(t *testing.T) (*mock.App, Keeper) {
|
||||||
types.NotBondedPoolName: {supply.Burner, supply.Staking},
|
types.NotBondedPoolName: {supply.Burner, supply.Staking},
|
||||||
types.BondedPoolName: {supply.Burner, supply.Staking},
|
types.BondedPoolName: {supply.Burner, supply.Staking},
|
||||||
}
|
}
|
||||||
supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bankKeeper, supply.DefaultCodespace, maccPerms)
|
supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bankKeeper, maccPerms)
|
||||||
keeper := NewKeeper(mApp.Cdc, keyStaking, tkeyStaking, supplyKeeper, mApp.ParamsKeeper.Subspace(DefaultParamspace), DefaultCodespace)
|
keeper := NewKeeper(mApp.Cdc, keyStaking, tkeyStaking, supplyKeeper, mApp.ParamsKeeper.Subspace(DefaultParamspace), DefaultCodespace)
|
||||||
|
|
||||||
mApp.Router().AddRoute(RouterKey, NewHandler(keeper))
|
mApp.Router().AddRoute(RouterKey, NewHandler(keeper))
|
||||||
|
|
|
@ -62,7 +62,7 @@ func (k Keeper) TotalBondedTokens(ctx sdk.Context) sdk.Int {
|
||||||
|
|
||||||
// StakingTokenSupply staking tokens from the total supply
|
// StakingTokenSupply staking tokens from the total supply
|
||||||
func (k Keeper) StakingTokenSupply(ctx sdk.Context) sdk.Int {
|
func (k Keeper) StakingTokenSupply(ctx sdk.Context) sdk.Int {
|
||||||
return k.supplyKeeper.GetSupply(ctx).Total.AmountOf(k.BondDenom(ctx))
|
return k.supplyKeeper.GetSupply(ctx).GetTotal().AmountOf(k.BondDenom(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
// BondedRatio the fraction of the staking tokens which are currently bonded
|
// BondedRatio the fraction of the staking tokens which are currently bonded
|
||||||
|
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/x/params"
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||||
"github.com/cosmos/cosmos-sdk/x/supply/exported"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// dummy addresses used for testing
|
// dummy addresses used for testing
|
||||||
|
@ -70,8 +69,7 @@ func MakeTestCodec() *codec.Codec {
|
||||||
// Register AppAccount
|
// Register AppAccount
|
||||||
cdc.RegisterInterface((*auth.Account)(nil), nil)
|
cdc.RegisterInterface((*auth.Account)(nil), nil)
|
||||||
cdc.RegisterConcrete(&auth.BaseAccount{}, "test/staking/BaseAccount", nil)
|
cdc.RegisterConcrete(&auth.BaseAccount{}, "test/staking/BaseAccount", nil)
|
||||||
cdc.RegisterInterface((*exported.ModuleAccountI)(nil), nil)
|
supply.RegisterCodec(cdc)
|
||||||
cdc.RegisterConcrete(&supply.ModuleAccount{}, "test/staking/ModuleAccount", nil)
|
|
||||||
codec.RegisterCrypto(cdc)
|
codec.RegisterCrypto(cdc)
|
||||||
|
|
||||||
return cdc
|
return cdc
|
||||||
|
@ -139,7 +137,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context
|
||||||
types.NotBondedPoolName: {supply.Burner, supply.Staking},
|
types.NotBondedPoolName: {supply.Burner, supply.Staking},
|
||||||
types.BondedPoolName: {supply.Burner, supply.Staking},
|
types.BondedPoolName: {supply.Burner, supply.Staking},
|
||||||
}
|
}
|
||||||
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bk, supply.DefaultCodespace, maccPerms)
|
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bk, maccPerms)
|
||||||
|
|
||||||
initTokens := sdk.TokensFromConsensusPower(initPower)
|
initTokens := sdk.TokensFromConsensusPower(initPower)
|
||||||
initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens))
|
initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens))
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||||
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
|
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
|
||||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
|
||||||
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
|
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ type AccountKeeper interface {
|
||||||
|
|
||||||
// SupplyKeeper defines the expected supply Keeper (noalias)
|
// SupplyKeeper defines the expected supply Keeper (noalias)
|
||||||
type SupplyKeeper interface {
|
type SupplyKeeper interface {
|
||||||
GetSupply(ctx sdk.Context) supply.Supply
|
GetSupply(ctx sdk.Context) supplyexported.SupplyI
|
||||||
|
|
||||||
GetModuleAddress(name string) sdk.AccAddress
|
GetModuleAddress(name string) sdk.AccAddress
|
||||||
GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI
|
GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI
|
||||||
|
|
|
@ -1,11 +1,29 @@
|
||||||
package exported
|
package exported
|
||||||
|
|
||||||
import "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||||
|
)
|
||||||
|
|
||||||
// ModuleAccountI defines an account interface for modules that hold tokens in an escrow
|
// ModuleAccountI defines an account interface for modules that hold tokens in an escrow
|
||||||
type ModuleAccountI interface {
|
type ModuleAccountI interface {
|
||||||
exported.Account
|
exported.Account
|
||||||
|
|
||||||
GetName() string
|
GetName() string
|
||||||
GetPermissions() []string
|
GetPermissions() []string
|
||||||
HasPermission(string) bool
|
HasPermission(string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SupplyI defines an inflationary supply interface for modules that handle
|
||||||
|
// token supply.
|
||||||
|
type SupplyI interface {
|
||||||
|
GetTotal() sdk.Coins
|
||||||
|
SetTotal(total sdk.Coins) SupplyI
|
||||||
|
|
||||||
|
Inflate(amount sdk.Coins) SupplyI
|
||||||
|
Deflate(amount sdk.Coins) SupplyI
|
||||||
|
|
||||||
|
String() string
|
||||||
|
ValidateBasic() error
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
// CONTRACT: all types of accounts must have been already initialized/created
|
// CONTRACT: all types of accounts must have been already initialized/created
|
||||||
func InitGenesis(ctx sdk.Context, keeper Keeper, ak types.AccountKeeper, data GenesisState) {
|
func InitGenesis(ctx sdk.Context, keeper Keeper, ak types.AccountKeeper, data GenesisState) {
|
||||||
// manually set the total supply based on accounts if not provided
|
// manually set the total supply based on accounts if not provided
|
||||||
if data.Supply.Total.Empty() {
|
if data.Supply.GetTotal().Empty() {
|
||||||
var totalSupply sdk.Coins
|
var totalSupply sdk.Coins
|
||||||
ak.IterateAccounts(ctx,
|
ak.IterateAccounts(ctx,
|
||||||
func(acc authexported.Account) (stop bool) {
|
func(acc authexported.Account) (stop bool) {
|
||||||
|
@ -19,8 +19,10 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, ak types.AccountKeeper, data Ge
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
data.Supply.Total = totalSupply
|
|
||||||
|
data.Supply = data.Supply.SetTotal(totalSupply)
|
||||||
}
|
}
|
||||||
|
|
||||||
keeper.SetSupply(ctx, data.Supply)
|
keeper.SetSupply(ctx, data.Supply)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,8 @@ func (k Keeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) sdk
|
||||||
|
|
||||||
// update total supply
|
// update total supply
|
||||||
supply := k.GetSupply(ctx)
|
supply := k.GetSupply(ctx)
|
||||||
supply.Inflate(amt)
|
supply = supply.Inflate(amt)
|
||||||
|
|
||||||
k.SetSupply(ctx, supply)
|
k.SetSupply(ctx, supply)
|
||||||
|
|
||||||
logger := k.Logger(ctx)
|
logger := k.Logger(ctx)
|
||||||
|
@ -135,7 +136,7 @@ func (k Keeper) BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) sdk
|
||||||
|
|
||||||
// update total supply
|
// update total supply
|
||||||
supply := k.GetSupply(ctx)
|
supply := k.GetSupply(ctx)
|
||||||
supply.Deflate(amt)
|
supply = supply.Deflate(amt)
|
||||||
k.SetSupply(ctx, supply)
|
k.SetSupply(ctx, supply)
|
||||||
|
|
||||||
logger := k.Logger(ctx)
|
logger := k.Logger(ctx)
|
||||||
|
|
|
@ -94,7 +94,7 @@ func TestMintCoins(t *testing.T) {
|
||||||
err := keeper.MintCoins(ctx, types.Minter, initCoins)
|
err := keeper.MintCoins(ctx, types.Minter, initCoins)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, initCoins, getCoinsByName(ctx, keeper, types.Minter))
|
require.Equal(t, initCoins, getCoinsByName(ctx, keeper, types.Minter))
|
||||||
require.Equal(t, initialSupply.Total.Add(initCoins), keeper.GetSupply(ctx).Total)
|
require.Equal(t, initialSupply.GetTotal().Add(initCoins), keeper.GetSupply(ctx).GetTotal())
|
||||||
|
|
||||||
// test same functionality on module account with multiple permissions
|
// test same functionality on module account with multiple permissions
|
||||||
initialSupply = keeper.GetSupply(ctx)
|
initialSupply = keeper.GetSupply(ctx)
|
||||||
|
@ -102,7 +102,7 @@ func TestMintCoins(t *testing.T) {
|
||||||
err = keeper.MintCoins(ctx, multiPermAcc.GetName(), initCoins)
|
err = keeper.MintCoins(ctx, multiPermAcc.GetName(), initCoins)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, initCoins, getCoinsByName(ctx, keeper, multiPermAcc.GetName()))
|
require.Equal(t, initCoins, getCoinsByName(ctx, keeper, multiPermAcc.GetName()))
|
||||||
require.Equal(t, initialSupply.Total.Add(initCoins), keeper.GetSupply(ctx).Total)
|
require.Equal(t, initialSupply.GetTotal().Add(initCoins), keeper.GetSupply(ctx).GetTotal())
|
||||||
|
|
||||||
require.Panics(t, func() { keeper.MintCoins(ctx, types.Burner, initCoins) })
|
require.Panics(t, func() { keeper.MintCoins(ctx, types.Burner, initCoins) })
|
||||||
}
|
}
|
||||||
|
@ -115,22 +115,22 @@ func TestBurnCoins(t *testing.T) {
|
||||||
keeper.SetModuleAccount(ctx, burnerAcc)
|
keeper.SetModuleAccount(ctx, burnerAcc)
|
||||||
|
|
||||||
initialSupply := keeper.GetSupply(ctx)
|
initialSupply := keeper.GetSupply(ctx)
|
||||||
initialSupply.Inflate(initCoins)
|
initialSupply = initialSupply.Inflate(initCoins)
|
||||||
keeper.SetSupply(ctx, initialSupply)
|
keeper.SetSupply(ctx, initialSupply)
|
||||||
|
|
||||||
require.Error(t, keeper.BurnCoins(ctx, "", initCoins), "no module account")
|
require.Error(t, keeper.BurnCoins(ctx, "", initCoins), "no module account")
|
||||||
require.Panics(t, func() { keeper.BurnCoins(ctx, types.Minter, initCoins) }, "invalid permission")
|
require.Panics(t, func() { keeper.BurnCoins(ctx, types.Minter, initCoins) }, "invalid permission")
|
||||||
require.Panics(t, func() { keeper.BurnCoins(ctx, randomPerm, initialSupply.Total) }, "random permission")
|
require.Panics(t, func() { keeper.BurnCoins(ctx, randomPerm, initialSupply.GetTotal()) }, "random permission")
|
||||||
require.Panics(t, func() { keeper.BurnCoins(ctx, types.Burner, initialSupply.Total) }, "insufficient coins")
|
require.Panics(t, func() { keeper.BurnCoins(ctx, types.Burner, initialSupply.GetTotal()) }, "insufficient coins")
|
||||||
|
|
||||||
err := keeper.BurnCoins(ctx, types.Burner, initCoins)
|
err := keeper.BurnCoins(ctx, types.Burner, initCoins)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, sdk.Coins(nil), getCoinsByName(ctx, keeper, types.Burner))
|
require.Equal(t, sdk.Coins(nil), getCoinsByName(ctx, keeper, types.Burner))
|
||||||
require.Equal(t, initialSupply.Total.Sub(initCoins), keeper.GetSupply(ctx).Total)
|
require.Equal(t, initialSupply.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal())
|
||||||
|
|
||||||
// test same functionality on module account with multiple permissions
|
// test same functionality on module account with multiple permissions
|
||||||
initialSupply = keeper.GetSupply(ctx)
|
initialSupply = keeper.GetSupply(ctx)
|
||||||
initialSupply.Inflate(initCoins)
|
initialSupply = initialSupply.Inflate(initCoins)
|
||||||
keeper.SetSupply(ctx, initialSupply)
|
keeper.SetSupply(ctx, initialSupply)
|
||||||
|
|
||||||
require.NoError(t, multiPermAcc.SetCoins(initCoins))
|
require.NoError(t, multiPermAcc.SetCoins(initCoins))
|
||||||
|
@ -139,5 +139,5 @@ func TestBurnCoins(t *testing.T) {
|
||||||
err = keeper.BurnCoins(ctx, multiPermAcc.GetName(), initCoins)
|
err = keeper.BurnCoins(ctx, multiPermAcc.GetName(), initCoins)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, sdk.Coins(nil), getCoinsByName(ctx, keeper, multiPermAcc.GetName()))
|
require.Equal(t, sdk.Coins(nil), getCoinsByName(ctx, keeper, multiPermAcc.GetName()))
|
||||||
require.Equal(t, initialSupply.Total.Sub(initCoins), keeper.GetSupply(ctx).Total)
|
require.Equal(t, initialSupply.GetTotal().Sub(initCoins), keeper.GetSupply(ctx).GetTotal())
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ func createTestInput(t *testing.T, isCheckTx bool, initPower int64, nAccs int64)
|
||||||
multiPerm: {types.Minter, types.Burner, types.Staking},
|
multiPerm: {types.Minter, types.Burner, types.Staking},
|
||||||
randomPerm: {"random"},
|
randomPerm: {"random"},
|
||||||
}
|
}
|
||||||
keeper := NewKeeper(cdc, keySupply, ak, bk, DefaultCodespace, maccPerms)
|
keeper := NewKeeper(cdc, keySupply, ak, bk, maccPerms)
|
||||||
totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, valTokens.MulRaw(nAccs)))
|
totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, valTokens.MulRaw(nAccs)))
|
||||||
keeper.SetSupply(ctx, types.NewSupply(totalSupply))
|
keeper.SetSupply(ctx, types.NewSupply(totalSupply))
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,12 @@ func TotalSupply(k Keeper) sdk.Invariant {
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
broken := !expectedTotal.IsEqual(supply.Total)
|
broken := !expectedTotal.IsEqual(supply.GetTotal())
|
||||||
|
|
||||||
return sdk.FormatInvariant(types.ModuleName, "total supply",
|
return sdk.FormatInvariant(types.ModuleName, "total supply",
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"\tsum of accounts coins: %v\n"+
|
"\tsum of accounts coins: %v\n"+
|
||||||
"\tsupply.Total: %v\n",
|
"\tsupply.Total: %v\n",
|
||||||
expectedTotal, supply.Total), broken)
|
expectedTotal, supply.GetTotal()), broken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,7 @@ type Keeper struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKeeper creates a new Keeper instance
|
// NewKeeper creates a new Keeper instance
|
||||||
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ak types.AccountKeeper, bk types.BankKeeper,
|
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ak types.AccountKeeper, bk types.BankKeeper, maccPerms map[string][]string) Keeper {
|
||||||
codespace sdk.CodespaceType, maccPerms map[string][]string) Keeper {
|
|
||||||
|
|
||||||
// set the addresses
|
// set the addresses
|
||||||
permAddrs := make(map[string]types.PermissionsForAddress)
|
permAddrs := make(map[string]types.PermissionsForAddress)
|
||||||
for name, perms := range maccPerms {
|
for name, perms := range maccPerms {
|
||||||
|
@ -45,7 +43,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSupply retrieves the Supply from store
|
// GetSupply retrieves the Supply from store
|
||||||
func (k Keeper) GetSupply(ctx sdk.Context) (supply types.Supply) {
|
func (k Keeper) GetSupply(ctx sdk.Context) (supply exported.SupplyI) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
b := store.Get(SupplyKey)
|
b := store.Get(SupplyKey)
|
||||||
if b == nil {
|
if b == nil {
|
||||||
|
@ -56,7 +54,7 @@ func (k Keeper) GetSupply(ctx sdk.Context) (supply types.Supply) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSupply sets the Supply to store
|
// SetSupply sets the Supply to store
|
||||||
func (k Keeper) SetSupply(ctx sdk.Context, supply types.Supply) {
|
func (k Keeper) SetSupply(ctx sdk.Context, supply exported.SupplyI) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
b := k.cdc.MustMarshalBinaryLengthPrefixed(supply)
|
b := k.cdc.MustMarshalBinaryLengthPrefixed(supply)
|
||||||
store.Set(SupplyKey, b)
|
store.Set(SupplyKey, b)
|
||||||
|
|
|
@ -16,7 +16,7 @@ func TestSupply(t *testing.T) {
|
||||||
|
|
||||||
ctx, _, keeper := createTestInput(t, false, initialPower, nAccs)
|
ctx, _, keeper := createTestInput(t, false, initialPower, nAccs)
|
||||||
|
|
||||||
total := keeper.GetSupply(ctx).Total
|
total := keeper.GetSupply(ctx).GetTotal()
|
||||||
expectedTotal := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens.MulRaw(nAccs)))
|
expectedTotal := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens.MulRaw(nAccs)))
|
||||||
|
|
||||||
require.Equal(t, expectedTotal, total)
|
require.Equal(t, expectedTotal, total)
|
||||||
|
|
|
@ -14,10 +14,13 @@ import (
|
||||||
func NewQuerier(k Keeper) sdk.Querier {
|
func NewQuerier(k Keeper) sdk.Querier {
|
||||||
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
|
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
|
||||||
switch path[0] {
|
switch path[0] {
|
||||||
|
|
||||||
case types.QueryTotalSupply:
|
case types.QueryTotalSupply:
|
||||||
return queryTotalSupply(ctx, req, k)
|
return queryTotalSupply(ctx, req, k)
|
||||||
|
|
||||||
case types.QuerySupplyOf:
|
case types.QuerySupplyOf:
|
||||||
return querySupplyOf(ctx, req, k)
|
return querySupplyOf(ctx, req, k)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, sdk.ErrUnknownRequest("unknown supply query endpoint")
|
return nil, sdk.ErrUnknownRequest("unknown supply query endpoint")
|
||||||
}
|
}
|
||||||
|
@ -32,7 +35,7 @@ func queryTotalSupply(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte,
|
||||||
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
|
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
totalSupply := k.GetSupply(ctx).Total
|
totalSupply := k.GetSupply(ctx).GetTotal()
|
||||||
|
|
||||||
start, end := client.Paginate(len(totalSupply), params.Page, params.Limit, 100)
|
start, end := client.Paginate(len(totalSupply), params.Page, params.Limit, 100)
|
||||||
if start < 0 || end < 0 {
|
if start < 0 || end < 0 {
|
||||||
|
@ -57,7 +60,7 @@ func querySupplyOf(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sd
|
||||||
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
|
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
supply := k.GetSupply(ctx).Total.AmountOf(params.Denom)
|
supply := k.GetSupply(ctx).GetTotal().AmountOf(params.Denom)
|
||||||
|
|
||||||
res, err := supply.MarshalJSON()
|
res, err := supply.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -8,7 +8,9 @@ import (
|
||||||
// RegisterCodec registers the account types and interface
|
// RegisterCodec registers the account types and interface
|
||||||
func RegisterCodec(cdc *codec.Codec) {
|
func RegisterCodec(cdc *codec.Codec) {
|
||||||
cdc.RegisterInterface((*exported.ModuleAccountI)(nil), nil)
|
cdc.RegisterInterface((*exported.ModuleAccountI)(nil), nil)
|
||||||
|
cdc.RegisterInterface((*exported.SupplyI)(nil), nil)
|
||||||
cdc.RegisterConcrete(&ModuleAccount{}, "cosmos-sdk/ModuleAccount", nil)
|
cdc.RegisterConcrete(&ModuleAccount{}, "cosmos-sdk/ModuleAccount", nil)
|
||||||
|
cdc.RegisterConcrete(&Supply{}, "cosmos-sdk/Supply", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModuleCdc generic sealed codec to be used throughout module
|
// ModuleCdc generic sealed codec to be used throughout module
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/supply/exported"
|
||||||
|
)
|
||||||
|
|
||||||
// GenesisState is the supply state that must be provided at genesis.
|
// GenesisState is the supply state that must be provided at genesis.
|
||||||
type GenesisState struct {
|
type GenesisState struct {
|
||||||
Supply Supply `json:"supply" yaml:"supply"`
|
Supply exported.SupplyI `json:"supply" yaml:"supply"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGenesisState creates a new genesis state.
|
// NewGenesisState creates a new genesis state.
|
||||||
func NewGenesisState(supply Supply) GenesisState {
|
func NewGenesisState(supply exported.SupplyI) GenesisState {
|
||||||
return GenesisState{supply}
|
return GenesisState{supply}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,30 +3,51 @@ package types
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/supply/exported"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Implements Delegation interface
|
||||||
|
var _ exported.SupplyI = Supply{}
|
||||||
|
|
||||||
// Supply represents a struct that passively keeps track of the total supply amounts in the network
|
// Supply represents a struct that passively keeps track of the total supply amounts in the network
|
||||||
type Supply struct {
|
type Supply struct {
|
||||||
Total sdk.Coins `json:"total" yaml:"total"` // total supply of tokens registered on the chain
|
Total sdk.Coins `json:"total" yaml:"total"` // total supply of tokens registered on the chain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetTotal sets the total supply.
|
||||||
|
func (supply Supply) SetTotal(total sdk.Coins) exported.SupplyI {
|
||||||
|
supply.Total = total
|
||||||
|
return supply
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTotal returns the supply total.
|
||||||
|
func (supply Supply) GetTotal() sdk.Coins {
|
||||||
|
return supply.Total
|
||||||
|
}
|
||||||
|
|
||||||
// NewSupply creates a new Supply instance
|
// NewSupply creates a new Supply instance
|
||||||
func NewSupply(total sdk.Coins) Supply { return Supply{total} }
|
func NewSupply(total sdk.Coins) exported.SupplyI {
|
||||||
|
return Supply{total}
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultSupply creates an empty Supply
|
// DefaultSupply creates an empty Supply
|
||||||
func DefaultSupply() Supply { return NewSupply(sdk.NewCoins()) }
|
func DefaultSupply() exported.SupplyI {
|
||||||
|
return NewSupply(sdk.NewCoins())
|
||||||
|
}
|
||||||
|
|
||||||
// Inflate adds coins to the total supply
|
// Inflate adds coins to the total supply
|
||||||
func (supply *Supply) Inflate(amount sdk.Coins) {
|
func (supply Supply) Inflate(amount sdk.Coins) exported.SupplyI {
|
||||||
supply.Total = supply.Total.Add(amount)
|
supply.Total = supply.Total.Add(amount)
|
||||||
|
return supply
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deflate subtracts coins from the total supply
|
// Deflate subtracts coins from the total supply
|
||||||
func (supply *Supply) Deflate(amount sdk.Coins) {
|
func (supply Supply) Deflate(amount sdk.Coins) exported.SupplyI {
|
||||||
supply.Total = supply.Total.Sub(amount)
|
supply.Total = supply.Total.Sub(amount)
|
||||||
|
return supply
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns a human readable string representation of a supplier.
|
// String returns a human readable string representation of a supplier.
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
func TestSupplyMarshalYAML(t *testing.T) {
|
func TestSupplyMarshalYAML(t *testing.T) {
|
||||||
supply := DefaultSupply()
|
supply := DefaultSupply()
|
||||||
coins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt()))
|
coins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt()))
|
||||||
supply.Inflate(coins)
|
supply = supply.Inflate(coins)
|
||||||
|
|
||||||
bz, err := yaml.Marshal(supply)
|
bz, err := yaml.Marshal(supply)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
Loading…
Reference in New Issue