github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/baseapp/abci.go (about)

     1  package baseapp
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"sort"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  	"syscall"
    12  	"time"
    13  
    14  	"github.com/fibonacci-chain/fbc/libs/system/trace/persist"
    15  	"github.com/spf13/viper"
    16  
    17  	"github.com/fibonacci-chain/fbc/app/rpc/simulator"
    18  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    19  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/mpt"
    20  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    21  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    22  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    23  	"github.com/fibonacci-chain/fbc/libs/iavl"
    24  	"github.com/fibonacci-chain/fbc/libs/system/trace"
    25  	abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
    26  	tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types"
    27  	"github.com/tendermint/go-amino"
    28  )
    29  
    30  // InitChain implements the ABCI interface. It runs the initialization logic
    31  // directly on the CommitMultiStore.
    32  func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) {
    33  	// stash the consensus params in the cms main store and memoize
    34  	if req.ConsensusParams != nil {
    35  		app.setConsensusParams(req.ConsensusParams)
    36  		app.storeConsensusParams(req.ConsensusParams)
    37  	}
    38  
    39  	initHeader := abci.Header{ChainID: req.ChainId, Time: req.Time}
    40  
    41  	// initialize the deliver state and check state with a correct header
    42  	app.setDeliverState(initHeader)
    43  	app.setCheckState(initHeader)
    44  
    45  	if app.initChainer == nil {
    46  		return
    47  	}
    48  
    49  	// add block gas meter for any genesis transactions (allow infinite gas)
    50  	app.deliverState.ctx.SetBlockGasMeter(sdk.NewInfiniteGasMeter())
    51  
    52  	res = app.initChainer(app.deliverState.ctx, req)
    53  
    54  	// sanity check
    55  	if len(req.Validators) > 0 {
    56  		if len(req.Validators) != len(res.Validators) {
    57  			panic(
    58  				fmt.Errorf(
    59  					"len(RequestInitChain.Validators) != len(GenesisValidators) (%d != %d)",
    60  					len(req.Validators), len(res.Validators),
    61  				),
    62  			)
    63  		}
    64  
    65  		sort.Sort(abci.ValidatorUpdates(req.Validators))
    66  		sort.Sort(abci.ValidatorUpdates(res.Validators))
    67  
    68  		for i, val := range res.Validators {
    69  			if !val.Equal(req.Validators[i]) {
    70  				panic(fmt.Errorf("genesisValidators[%d] != req.Validators[%d] ", i, i))
    71  			}
    72  		}
    73  	}
    74  
    75  	// NOTE: We don't commit, but BeginBlock for block 1 starts from this
    76  	// deliverState.
    77  	return res
    78  }
    79  
    80  // Info implements the ABCI interface.
    81  func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo {
    82  	lastCommitID := app.cms.LastCommitID()
    83  
    84  	return abci.ResponseInfo{
    85  		Data:             app.name,
    86  		LastBlockHeight:  lastCommitID.Version,
    87  		LastBlockAppHash: lastCommitID.Hash,
    88  	}
    89  }
    90  
    91  // SetOption implements the ABCI interface.
    92  func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOption) {
    93  	// TODO: Implement!
    94  	switch req.Key {
    95  	case "ResetCheckState":
    96  		// reset check state
    97  		app.setCheckState(app.checkState.ctx.BlockHeader())
    98  	default:
    99  		// do nothing
   100  	}
   101  	return
   102  }
   103  
   104  // FilterPeerByAddrPort filters peers by address/port.
   105  func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery {
   106  	if app.addrPeerFilter != nil {
   107  		return app.addrPeerFilter(info)
   108  	}
   109  	return abci.ResponseQuery{}
   110  }
   111  
   112  // FilterPeerByIDfilters peers by node ID.
   113  func (app *BaseApp) FilterPeerByID(info string) abci.ResponseQuery {
   114  	if app.idPeerFilter != nil {
   115  		return app.idPeerFilter(info)
   116  	}
   117  	return abci.ResponseQuery{}
   118  }
   119  
   120  // BeginBlock implements the ABCI application interface.
   121  func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) {
   122  	app.blockDataCache.Clear()
   123  	app.PutCacheMultiStore(nil)
   124  	if app.cms.TracingEnabled() {
   125  		app.cms.SetTracingContext(sdk.TraceContext(
   126  			map[string]interface{}{"blockHeight": req.Header.Height},
   127  		))
   128  	}
   129  
   130  	if err := app.validateHeight(req); err != nil {
   131  		panic(err)
   132  	}
   133  
   134  	// Initialize the DeliverTx state. If this is the first block, it should
   135  	// already be initialized in InitChain. Otherwise app.deliverState will be
   136  	// nil, since it is reset on Commit.
   137  	if req.Header.Height > 1+tmtypes.GetStartBlockHeight() {
   138  		if app.deliverState != nil {
   139  			app.logger.Info(
   140  				"deliverState was not reset by BaseApp.Commit due to the previous prerun task being stopped",
   141  				"height", req.Header.Height)
   142  		}
   143  		app.setDeliverState(req.Header)
   144  	} else {
   145  		// In the first block, app.deliverState.ctx will already be initialized
   146  		// by InitChain. Context is now updated with Header information.
   147  		app.deliverState.ctx.
   148  			SetBlockHeader(req.Header).
   149  			SetBlockHeight(req.Header.Height)
   150  	}
   151  
   152  	app.newBlockCache()
   153  	// add block gas meter
   154  	var gasMeter sdk.GasMeter
   155  	if maxGas := app.getMaximumBlockGas(); maxGas > 0 {
   156  		gasMeter = sdk.NewGasMeter(maxGas)
   157  	} else {
   158  		gasMeter = sdk.NewInfiniteGasMeter()
   159  	}
   160  
   161  	app.deliverState.ctx.SetBlockGasMeter(gasMeter)
   162  
   163  	if app.beginBlocker != nil {
   164  		res = app.beginBlocker(app.deliverState.ctx, req)
   165  	}
   166  
   167  	// set the signed validators for addition to context in deliverTx
   168  	app.voteInfos = req.LastCommitInfo.GetVotes()
   169  
   170  	app.anteTracer = trace.NewTracer(trace.AnteChainDetail)
   171  
   172  	app.feeCollector = sdk.Coins{}
   173  	app.feeChanged = false
   174  	// clean FeeSplitCollector
   175  	app.FeeSplitCollector = make([]*sdk.FeeSplitInfo, 0)
   176  
   177  	return res
   178  }
   179  
   180  func (app *BaseApp) UpdateFeeCollector(fee sdk.Coins, add bool) {
   181  	if fee.IsZero() {
   182  		return
   183  	}
   184  	app.feeChanged = true
   185  	if add {
   186  		app.feeCollector = app.feeCollector.Add(fee...)
   187  	} else {
   188  		app.feeCollector = app.feeCollector.Sub(fee)
   189  	}
   190  }
   191  
   192  func (app *BaseApp) updateFeeCollectorAccount(isEndBlock bool) {
   193  	if app.updateFeeCollectorAccHandler == nil || !app.feeChanged {
   194  		return
   195  	}
   196  
   197  	defer func() {
   198  		if r := recover(); r != nil {
   199  			err := fmt.Errorf("panic: %v", r)
   200  			app.logger.Error("update fee collector account failed", "err", err)
   201  		}
   202  	}()
   203  
   204  	ctx, cache := app.cacheTxContext(app.getContextForTx(runTxModeDeliver, []byte{}), []byte{})
   205  	if isEndBlock {
   206  		// The feesplit is only processed at the endblock
   207  		if err := app.updateFeeCollectorAccHandler(ctx, app.feeCollector, app.FeeSplitCollector); err != nil {
   208  			panic(err)
   209  		}
   210  	} else {
   211  		if err := app.updateFeeCollectorAccHandler(ctx, app.feeCollector, nil); err != nil {
   212  			panic(err)
   213  		}
   214  	}
   215  	cache.Write()
   216  }
   217  
   218  // EndBlock implements the ABCI interface.
   219  func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
   220  	app.updateFeeCollectorAccount(true)
   221  
   222  	if app.deliverState.ms.TracingEnabled() {
   223  		app.deliverState.ms = app.deliverState.ms.SetTracingContext(nil).(sdk.CacheMultiStore)
   224  	}
   225  
   226  	if app.endBlocker != nil {
   227  		res = app.endBlocker(app.deliverState.ctx, req)
   228  	}
   229  
   230  	return
   231  }
   232  
   233  func (app *BaseApp) addCommitTraceInfo() {
   234  	nodeReadCountStr := strconv.Itoa(app.cms.GetNodeReadCount())
   235  	dbReadCountStr := strconv.Itoa(app.cms.GetDBReadCount())
   236  	dbReadTimeStr := strconv.FormatInt(time.Duration(app.cms.GetDBReadTime()).Milliseconds(), 10)
   237  	dbWriteCountStr := strconv.Itoa(app.cms.GetDBWriteCount())
   238  
   239  	iavlInfo := strings.Join([]string{"getnode<", nodeReadCountStr, ">, rdb<", dbReadCountStr, ">, rdbTs<", dbReadTimeStr, "ms>, savenode<", dbWriteCountStr, ">"}, "")
   240  
   241  	elapsedInfo := trace.GetElapsedInfo()
   242  	elapsedInfo.AddInfo(trace.Iavl, iavlInfo)
   243  
   244  	flatKvReadCountStr := strconv.Itoa(app.cms.GetFlatKVReadCount())
   245  	flatKvReadTimeStr := strconv.FormatInt(time.Duration(app.cms.GetFlatKVReadTime()).Milliseconds(), 10)
   246  	flatKvWriteCountStr := strconv.Itoa(app.cms.GetFlatKVWriteCount())
   247  	flatKvWriteTimeStr := strconv.FormatInt(time.Duration(app.cms.GetFlatKVWriteTime()).Milliseconds(), 10)
   248  
   249  	flatInfo := strings.Join([]string{"rflat<", flatKvReadCountStr, ">, rflatTs<", flatKvReadTimeStr, "ms>, wflat<", flatKvWriteCountStr, ">, wflatTs<", flatKvWriteTimeStr, "ms>"}, "")
   250  
   251  	elapsedInfo.AddInfo(trace.FlatKV, flatInfo)
   252  
   253  	//rtx := float64(atomic.LoadInt64(&app.checkTxNum))
   254  	//wtx := float64(atomic.LoadInt64(&app.wrappedCheckTxNum))
   255  
   256  	//elapsedInfo.AddInfo(trace.WtxRatio,
   257  	//	amino.BytesToStr(strconv.AppendFloat(make([]byte, 0, 4), wtx/(wtx+rtx), 'f', 2, 32)),
   258  	//)
   259  
   260  	readCache := float64(tmtypes.SignatureCache().ReadCount())
   261  	hitCache := float64(tmtypes.SignatureCache().HitCount())
   262  
   263  	elapsedInfo.AddInfo(trace.SigCacheRatio,
   264  		amino.BytesToStr(strconv.AppendFloat(make([]byte, 0, 4), hitCache/readCache, 'f', 2, 32)),
   265  	)
   266  
   267  	elapsedInfo.AddInfo(trace.AnteChainDetail, app.anteTracer.FormatRepeatingPins(sdk.AnteTerminatorTag))
   268  }
   269  
   270  // Commit implements the ABCI interface. It will commit all state that exists in
   271  // the deliver state's multi-store and includes the resulting commit ID in the
   272  // returned abci.ResponseCommit. Commit will set the check state based on the
   273  // latest header and reset the deliver state. Also, if a non-zero halt height is
   274  // defined in config, Commit will execute a deferred function call to check
   275  // against that height and gracefully halt if it matches the latest committed
   276  // height.
   277  func (app *BaseApp) Commit(req abci.RequestCommit) abci.ResponseCommit {
   278  
   279  	persist.GetStatistics().Init(trace.PreChange, trace.FlushCache, trace.CommitStores, trace.FlushMeta)
   280  	defer func() {
   281  		trace.GetElapsedInfo().AddInfo(trace.PersistDetails, persist.GetStatistics().Format())
   282  	}()
   283  	header := app.deliverState.ctx.BlockHeader()
   284  
   285  	if app.mptCommitHandler != nil {
   286  		app.mptCommitHandler(app.deliverState.ctx)
   287  	}
   288  	if mptStore := app.cms.GetCommitKVStore(sdk.NewKVStoreKey(mpt.StoreKey)); mptStore != nil {
   289  		// notify mptStore to tryUpdateTrie, must call before app.deliverState.ms.Write()
   290  		mpt.GAccTryUpdateTrieChannel <- struct{}{}
   291  		<-mpt.GAccTrieUpdatedChannel
   292  	}
   293  
   294  	// Write the DeliverTx state which is cache-wrapped and commit the MultiStore.
   295  	// The write to the DeliverTx state writes all state transitions to the root
   296  	// MultiStore (app.cms) so when Commit() is called is persists those values.
   297  	app.commitBlockCache()
   298  	app.deliverState.ms.Write()
   299  
   300  	var input iavl.TreeDeltaMap
   301  	if tmtypes.DownloadDelta && req.DeltaMap != nil {
   302  		var ok bool
   303  		input, ok = req.DeltaMap.(iavl.TreeDeltaMap)
   304  		if !ok {
   305  			panic("use TreeDeltaMap failed")
   306  		}
   307  	}
   308  
   309  	commitID, output := app.cms.CommitterCommitMap(input) // CommitterCommitMap
   310  
   311  	app.addCommitTraceInfo()
   312  
   313  	app.cms.ResetCount()
   314  	app.logger.Debug("Commit synced", "commit", amino.BytesHexStringer(commitID.Hash))
   315  
   316  	// Reset the Check state to the latest committed.
   317  	//
   318  	// NOTE: This is safe because Tendermint holds a lock on the mempool for
   319  	// Commit. Use the header from this latest block.
   320  	app.setCheckState(header)
   321  
   322  	app.logger.Debug("deliverState reset by BaseApp.Commit", "height", header.Height)
   323  	// empty/reset the deliver state
   324  	app.deliverState = nil
   325  
   326  	var halt bool
   327  
   328  	switch {
   329  	case app.haltHeight > 0 && uint64(header.Height) >= app.haltHeight:
   330  		halt = true
   331  
   332  	case app.haltTime > 0 && header.Time.Unix() >= int64(app.haltTime):
   333  		halt = true
   334  	}
   335  
   336  	if halt {
   337  		// Halt the binary and allow Tendermint to receive the ResponseCommit
   338  		// response with the commit ID hash. This will allow the node to successfully
   339  		// restart and process blocks assuming the halt configuration has been
   340  		// reset or moved to a more distant value.
   341  		app.halt()
   342  	}
   343  
   344  	return abci.ResponseCommit{
   345  		Data:     commitID.Hash,
   346  		DeltaMap: output,
   347  	}
   348  }
   349  
   350  // halt attempts to gracefully shutdown the node via SIGINT and SIGTERM falling
   351  // back on os.Exit if both fail.
   352  func (app *BaseApp) halt() {
   353  	app.logger.Info("halting node per configuration", "height", app.haltHeight, "time", app.haltTime)
   354  
   355  	p, err := os.FindProcess(os.Getpid())
   356  	if err == nil {
   357  		// attempt cascading signals in case SIGINT fails (os dependent)
   358  		sigIntErr := p.Signal(syscall.SIGINT)
   359  		sigTermErr := p.Signal(syscall.SIGTERM)
   360  		//Make sure the TrapSignal execute first
   361  		time.Sleep(50 * time.Millisecond)
   362  		if sigIntErr == nil || sigTermErr == nil {
   363  			return
   364  		}
   365  	}
   366  
   367  	// Resort to exiting immediately if the process could not be found or killed
   368  	// via SIGINT/SIGTERM signals.
   369  	app.logger.Info("failed to send SIGINT/SIGTERM; exiting...")
   370  	os.Exit(0)
   371  }
   372  
   373  // Query implements the ABCI interface. It delegates to CommitMultiStore if it
   374  // implements Queryable.
   375  func (app *BaseApp) Query(req abci.RequestQuery) abci.ResponseQuery {
   376  	ceptor := app.interceptors[req.Path]
   377  	if nil != ceptor {
   378  		// interceptor is like `aop`,it may record the request or rewrite the data in the request
   379  		// it should have funcs like `Begin` `End`,
   380  		// but for now, we will just redirect the path router,so once the request was intercepted(see #makeInterceptors),
   381  		// grpcQueryRouter#Route will return nil
   382  		ceptor.Intercept(&req)
   383  	}
   384  	path := splitPath(req.Path)
   385  	if len(path) == 0 {
   386  		return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no query path provided"))
   387  	}
   388  
   389  	if grpcHandler := app.grpcQueryRouter.Route(req.Path); grpcHandler != nil {
   390  		return app.handleQueryGRPC(grpcHandler, req)
   391  	}
   392  
   393  	switch path[0] {
   394  	// "/app" prefix for special application queries
   395  	case "app":
   396  		return handleQueryApp(app, path, req)
   397  
   398  	case "store":
   399  		return handleQueryStore(app, path, req)
   400  
   401  	case "p2p":
   402  		return handleQueryP2P(app, path)
   403  
   404  	case "custom":
   405  		return handleQueryCustom(app, path, req)
   406  	default:
   407  		return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query path"))
   408  	}
   409  }
   410  func handleSimulate(app *BaseApp, path []string, height int64, txBytes []byte, overrideBytes []byte) abci.ResponseQuery {
   411  	// if path contains address, it means 'eth_estimateGas' the sender
   412  	hasExtraPaths := len(path) > 2
   413  	var from string
   414  	if hasExtraPaths {
   415  		if addr, err := sdk.AccAddressFromBech32(path[2]); err == nil {
   416  			if err = sdk.VerifyAddressFormat(addr); err == nil {
   417  				from = path[2]
   418  			}
   419  		}
   420  	}
   421  
   422  	var tx sdk.Tx
   423  	var err error
   424  	if mem := GetGlobalMempool(); mem != nil {
   425  		tx, _ = mem.ReapEssentialTx(txBytes).(sdk.Tx)
   426  	}
   427  	if tx == nil {
   428  		tx, err = app.txDecoder(txBytes)
   429  		if err != nil {
   430  			return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to decode tx"))
   431  		}
   432  	}
   433  
   434  	msgs := tx.GetMsgs()
   435  
   436  	if enableFastQuery() {
   437  		isPureWasm := true
   438  		for _, msg := range msgs {
   439  			if msg.Route() != "wasm" {
   440  				isPureWasm = false
   441  				break
   442  			}
   443  		}
   444  		if isPureWasm {
   445  			wasmSimulator := simulator.NewWasmSimulator()
   446  			wasmSimulator.Context().GasMeter().ConsumeGas(73000, "general ante check cost")
   447  			wasmSimulator.Context().GasMeter().ConsumeGas(uint64(10*len(txBytes)), "tx size cost")
   448  			res, err := wasmSimulator.Simulate(msgs)
   449  			if err != nil {
   450  				return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to simulate wasm tx"))
   451  			}
   452  
   453  			gasMeter := wasmSimulator.Context().GasMeter()
   454  			simRes := sdk.SimulationResponse{
   455  				GasInfo: sdk.GasInfo{
   456  					GasUsed: gasMeter.GasConsumed(),
   457  				},
   458  				Result: res,
   459  			}
   460  			return abci.ResponseQuery{
   461  				Codespace: sdkerrors.RootCodespace,
   462  				Height:    height,
   463  				Value:     codec.Cdc.MustMarshalBinaryBare(simRes),
   464  			}
   465  		}
   466  
   467  	}
   468  	gInfo, res, err := app.Simulate(txBytes, tx, height, overrideBytes, from)
   469  
   470  	// if path contains mempool, it means to enable MaxGasUsedPerBlock
   471  	// return the actual gasUsed even though simulate tx failed
   472  	isMempoolSim := hasExtraPaths && path[2] == "mempool"
   473  	if err != nil && !isMempoolSim {
   474  		return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to simulate tx"))
   475  	}
   476  
   477  	simRes := sdk.SimulationResponse{
   478  		GasInfo: gInfo,
   479  		Result:  res,
   480  	}
   481  
   482  	return abci.ResponseQuery{
   483  		Codespace: sdkerrors.RootCodespace,
   484  		Height:    height,
   485  		Value:     codec.Cdc.MustMarshalBinaryBare(simRes),
   486  	}
   487  }
   488  func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery {
   489  	if len(path) >= 2 {
   490  		switch path[1] {
   491  		case "simulate":
   492  			return handleSimulate(app, path, req.Height, req.Data, nil)
   493  
   494  		case "simulateWithOverrides":
   495  			queryBytes := req.Data
   496  			var queryData types.SimulateData
   497  			if err := json.Unmarshal(queryBytes, &queryData); err != nil {
   498  				return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to decode simulateOverrideData"))
   499  			}
   500  			return handleSimulate(app, path, req.Height, queryData.TxBytes, queryData.OverridesBytes)
   501  
   502  		case "trace":
   503  			var queryParam sdk.QueryTraceTx
   504  			err := json.Unmarshal(req.Data, &queryParam)
   505  			if err != nil {
   506  				return sdkerrors.QueryResult(sdkerrors.Wrap(err, "invalid trace tx params"))
   507  			}
   508  			tmtx, err := GetABCITx(queryParam.TxHash.Bytes())
   509  			if err != nil {
   510  				return sdkerrors.QueryResult(sdkerrors.Wrap(err, "invalid trace tx bytes"))
   511  			}
   512  			tx, err := app.txDecoder(tmtx.Tx, tmtx.Height)
   513  			if err != nil {
   514  				return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to decode tx"))
   515  			}
   516  			block, err := GetABCIBlock(tmtx.Height)
   517  			if err != nil {
   518  				return sdkerrors.QueryResult(sdkerrors.Wrap(err, "invalid trace tx block header"))
   519  			}
   520  			res, err := app.TraceTx(queryParam, tx, tmtx.Index, block.Block)
   521  			if err != nil {
   522  				return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to trace tx"))
   523  			}
   524  			return abci.ResponseQuery{
   525  				Codespace: sdkerrors.RootCodespace,
   526  				Height:    req.Height,
   527  				Value:     codec.Cdc.MustMarshalBinaryBare(res),
   528  			}
   529  
   530  		case "version":
   531  			return abci.ResponseQuery{
   532  				Codespace: sdkerrors.RootCodespace,
   533  				Height:    req.Height,
   534  				Value:     []byte(app.appVersion),
   535  			}
   536  
   537  		default:
   538  			return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query: %s", path))
   539  		}
   540  	}
   541  
   542  	return sdkerrors.QueryResult(
   543  		sdkerrors.Wrap(
   544  			sdkerrors.ErrUnknownRequest,
   545  			"expected second parameter to be either 'simulate' or 'version', neither was present",
   546  		),
   547  	)
   548  }
   549  
   550  func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery {
   551  	// "/store" prefix for store queries
   552  	queryable, ok := app.cms.(sdk.Queryable)
   553  	if !ok {
   554  		return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "multistore doesn't support queries"))
   555  	}
   556  
   557  	req.Path = "/" + strings.Join(path[1:], "/")
   558  
   559  	// when a client did not provide a query height, manually inject the latest
   560  	if req.Height == 0 {
   561  		req.Height = app.LastBlockHeight()
   562  	}
   563  
   564  	if req.Height <= 1 && req.Prove {
   565  		return sdkerrors.QueryResult(
   566  			sdkerrors.Wrap(
   567  				sdkerrors.ErrInvalidRequest,
   568  				"cannot query with proof when height <= 1; please provide a valid height",
   569  			),
   570  		)
   571  	}
   572  
   573  	resp := queryable.Query(req)
   574  	resp.Height = req.Height
   575  
   576  	return resp
   577  }
   578  
   579  func handleQueryP2P(app *BaseApp, path []string) abci.ResponseQuery {
   580  	// "/p2p" prefix for p2p queries
   581  	if len(path) >= 4 {
   582  		cmd, typ, arg := path[1], path[2], path[3]
   583  		switch cmd {
   584  		case "filter":
   585  			switch typ {
   586  			case "addr":
   587  				return app.FilterPeerByAddrPort(arg)
   588  
   589  			case "id":
   590  				return app.FilterPeerByID(arg)
   591  			}
   592  
   593  		default:
   594  			return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "expected second parameter to be 'filter'"))
   595  		}
   596  	}
   597  
   598  	return sdkerrors.QueryResult(
   599  		sdkerrors.Wrap(
   600  			sdkerrors.ErrUnknownRequest, "expected path is p2p filter <addr|id> <parameter>",
   601  		),
   602  	)
   603  }
   604  
   605  func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery {
   606  	// path[0] should be "custom" because "/custom" prefix is required for keeper
   607  	// queries.
   608  	//
   609  	// The QueryRouter routes using path[1]. For example, in the path
   610  	// "custom/gov/proposal", QueryRouter routes using "gov".
   611  	if len(path) < 2 || path[1] == "" {
   612  		return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no route for custom query specified"))
   613  	}
   614  
   615  	querier := app.queryRouter.Route(path[1])
   616  	if querier == nil {
   617  		return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "no custom querier found for route %s", path[1]))
   618  	}
   619  
   620  	// when a client did not provide a query height, manually inject the latest
   621  	if req.Height == 0 {
   622  		req.Height = app.LastBlockHeight()
   623  	}
   624  
   625  	if req.Height <= 1 && req.Prove {
   626  		return sdkerrors.QueryResult(
   627  			sdkerrors.Wrap(
   628  				sdkerrors.ErrInvalidRequest,
   629  				"cannot query with proof when height <= 1; please provide a valid height",
   630  			),
   631  		)
   632  	}
   633  
   634  	cacheMS, err := app.cms.CacheMultiStoreWithVersion(req.Height)
   635  	if err != nil {
   636  		return sdkerrors.QueryResult(
   637  			sdkerrors.Wrapf(
   638  				sdkerrors.ErrInvalidRequest,
   639  				"failed to load state at height %d; %s (latest height: %d)", req.Height, err, app.LastBlockHeight(),
   640  			),
   641  		)
   642  	}
   643  
   644  	// cache wrap the commit-multistore for safety
   645  	ctx := sdk.NewContext(
   646  		cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger,
   647  	)
   648  	ctx.SetMinGasPrices(app.minGasPrices)
   649  	ctx.SetBlockHeight(req.Height)
   650  
   651  	// Passes the rest of the path as an argument to the querier.
   652  	//
   653  	// For example, in the path "custom/gov/proposal/test", the gov querier gets
   654  	// []string{"proposal", "test"} as the path.
   655  	resBytes, err := querier(ctx, path[2:], req)
   656  	if err != nil {
   657  		space, code, log := sdkerrors.ABCIInfo(err, false)
   658  		return abci.ResponseQuery{
   659  			Code:      code,
   660  			Codespace: space,
   661  			Log:       log,
   662  			Height:    req.Height,
   663  		}
   664  	}
   665  
   666  	return abci.ResponseQuery{
   667  		Height: req.Height,
   668  		Value:  resBytes,
   669  	}
   670  }
   671  
   672  // splitPath splits a string path using the delimiter '/'.
   673  //
   674  // e.g. "this/is/funny" becomes []string{"this", "is", "funny"}
   675  func splitPath(requestPath string) (path []string) {
   676  	path = strings.Split(requestPath, "/")
   677  
   678  	// first element is empty string
   679  	if len(path) > 0 && path[0] == "" {
   680  		path = path[1:]
   681  	}
   682  
   683  	return path
   684  }
   685  
   686  var (
   687  	fastQuery bool
   688  	fqOnce    sync.Once
   689  )
   690  
   691  func enableFastQuery() bool {
   692  	fqOnce.Do(func() {
   693  		fastQuery = viper.GetBool("fast-query")
   694  	})
   695  	return fastQuery
   696  }