github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/sdk/baseapp.go (about)

     1  package sdk
     2  
     3  import (
     4  	"fmt"
     5  	"log/slog"
     6  	"os"
     7  	"runtime/debug"
     8  	"sort"
     9  	"strings"
    10  	"syscall"
    11  
    12  	"github.com/gnolang/gno/tm2/pkg/amino"
    13  	abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types"
    14  	bft "github.com/gnolang/gno/tm2/pkg/bft/types"
    15  	dbm "github.com/gnolang/gno/tm2/pkg/db"
    16  	"github.com/gnolang/gno/tm2/pkg/errors"
    17  	"github.com/gnolang/gno/tm2/pkg/std"
    18  	"github.com/gnolang/gno/tm2/pkg/store"
    19  )
    20  
    21  // Key to store the consensus params in the main store.
    22  var (
    23  	mainConsensusParamsKey = []byte("consensus_params")
    24  	mainLastHeaderKey      = []byte("last_header")
    25  )
    26  
    27  // BaseApp reflects the ABCI application implementation.
    28  type BaseApp struct {
    29  	// initialized on creation
    30  	logger *slog.Logger
    31  	name   string                 // application name from abci.Info
    32  	db     dbm.DB                 // common DB backend
    33  	cms    store.CommitMultiStore // Main (uncached) state
    34  	router Router                 // handle any kind of message
    35  
    36  	// set upon LoadVersion or LoadLatestVersion.
    37  	baseKey store.StoreKey // Base Store in cms (raw db, not hashed)
    38  	mainKey store.StoreKey // Main Store in cms (e.g. iavl, merkle-ized)
    39  
    40  	anteHandler  AnteHandler  // ante handler for fee and auth
    41  	initChainer  InitChainer  // initialize state with validators and state blob
    42  	beginBlocker BeginBlocker // logic to run before any txs
    43  	endBlocker   EndBlocker   // logic to run after all txs, and to determine valset changes
    44  
    45  	// --------------------
    46  	// Volatile state
    47  	// checkState is set on initialization and reset on Commit.
    48  	// deliverState is set in InitChain and BeginBlock and cleared on Commit.
    49  	// See methods setCheckState and setDeliverState.
    50  	checkState   *state          // for CheckTx
    51  	deliverState *state          // for DeliverTx
    52  	voteInfos    []abci.VoteInfo // absent validators from begin block
    53  
    54  	// consensus params
    55  	// TODO: Move this in the future to baseapp param store on main store.
    56  	consensusParams *abci.ConsensusParams
    57  
    58  	// The minimum gas prices a validator is willing to accept for processing a
    59  	// transaction. This is mainly used for DoS and spam prevention.
    60  	minGasPrices []GasPrice
    61  
    62  	// flag for sealing options and parameters to a BaseApp
    63  	sealed bool // TODO: needed?
    64  
    65  	// block height at which to halt the chain and gracefully shutdown
    66  	haltHeight uint64
    67  
    68  	// minimum block time (in Unix seconds) at which to halt the chain and gracefully shutdown
    69  	haltTime uint64
    70  
    71  	// application's version string
    72  	appVersion string
    73  }
    74  
    75  var _ abci.Application = (*BaseApp)(nil)
    76  
    77  // NewBaseApp returns a reference to an initialized BaseApp. It accepts a
    78  // variadic number of option functions, which act on the BaseApp to set
    79  // configuration choices.
    80  //
    81  // NOTE: The db is used to store the version number for now.
    82  func NewBaseApp(
    83  	name string,
    84  	logger *slog.Logger,
    85  	db dbm.DB,
    86  	baseKey store.StoreKey,
    87  	mainKey store.StoreKey,
    88  	options ...func(*BaseApp),
    89  ) *BaseApp {
    90  	app := &BaseApp{
    91  		logger:  logger,
    92  		name:    name,
    93  		db:      db,
    94  		cms:     store.NewCommitMultiStore(db),
    95  		router:  NewRouter(),
    96  		baseKey: baseKey,
    97  		mainKey: mainKey,
    98  	}
    99  	for _, option := range options {
   100  		option(app)
   101  	}
   102  
   103  	return app
   104  }
   105  
   106  // Name returns the name of the BaseApp.
   107  func (app *BaseApp) Name() string {
   108  	return app.name
   109  }
   110  
   111  // AppVersion returns the application's version string.
   112  func (app *BaseApp) AppVersion() string {
   113  	return app.appVersion
   114  }
   115  
   116  // Logger returns the logger of the BaseApp.
   117  func (app *BaseApp) Logger() *slog.Logger {
   118  	return app.logger
   119  }
   120  
   121  // MountStoreWithDB mounts a store to the provided key in the BaseApp
   122  // multistore, using a specified DB.
   123  func (app *BaseApp) MountStoreWithDB(key store.StoreKey, cons store.CommitStoreConstructor, db dbm.DB) {
   124  	app.cms.MountStoreWithDB(key, cons, db)
   125  }
   126  
   127  // MountStore mounts a store to the provided key in the BaseApp multistore,
   128  // using the default DB.
   129  func (app *BaseApp) MountStore(key store.StoreKey, cons store.CommitStoreConstructor) {
   130  	app.cms.MountStoreWithDB(key, cons, nil)
   131  }
   132  
   133  // LoadLatestVersion loads the latest application version. It will panic if
   134  // called more than once on a running BaseApp.
   135  // This, or LoadVersion() MUST be called even after first init.
   136  func (app *BaseApp) LoadLatestVersion() error {
   137  	err := app.cms.LoadLatestVersion()
   138  	if err != nil {
   139  		return err
   140  	}
   141  	return app.initFromMainStore()
   142  }
   143  
   144  // LoadVersion loads the BaseApp application version. It will panic if called
   145  // more than once on a running baseapp.
   146  // This, or LoadLatestVersion() MUST be called even after first init.
   147  func (app *BaseApp) LoadVersion(version int64) error {
   148  	err := app.cms.LoadVersion(version)
   149  	if err != nil {
   150  		return err
   151  	}
   152  	return app.initFromMainStore()
   153  }
   154  
   155  // LastCommitID returns the last CommitID of the multistore.
   156  func (app *BaseApp) LastCommitID() store.CommitID {
   157  	return app.cms.LastCommitID()
   158  }
   159  
   160  // LastBlockHeight returns the last committed block height.
   161  func (app *BaseApp) LastBlockHeight() int64 {
   162  	return app.cms.LastCommitID().Version
   163  }
   164  
   165  // initializes the app from app.cms after loading.
   166  func (app *BaseApp) initFromMainStore() error {
   167  	baseStore := app.cms.GetStore(app.baseKey)
   168  	if baseStore == nil {
   169  		return errors.New("baseapp expects MultiStore with 'base' Store")
   170  	}
   171  	mainStore := app.cms.GetStore(app.mainKey)
   172  	if mainStore == nil {
   173  		return errors.New("baseapp expects MultiStore with 'main' Store")
   174  	}
   175  
   176  	// Load the consensus params from the main store. If the consensus params are
   177  	// nil, it will be saved later during InitChain.
   178  	//
   179  	// TODO: assert that InitChain hasn't yet been called.
   180  	consensusParamsBz := mainStore.Get(mainConsensusParamsKey)
   181  	if consensusParamsBz != nil {
   182  		consensusParams := &abci.ConsensusParams{}
   183  		err := amino.Unmarshal(consensusParamsBz, consensusParams)
   184  		if err != nil {
   185  			panic(err)
   186  		}
   187  
   188  		app.setConsensusParams(consensusParams)
   189  	}
   190  
   191  	// Load the consensus header from the main store.
   192  	// This is needed to setCheckState with the right chainID etc.
   193  	lastHeaderBz := baseStore.Get(mainLastHeaderKey)
   194  	if lastHeaderBz != nil {
   195  		lastHeader := &bft.Header{}
   196  		err := amino.Unmarshal(lastHeaderBz, lastHeader)
   197  		if err != nil {
   198  			panic(err)
   199  		}
   200  		app.setCheckState(lastHeader)
   201  	}
   202  	// Done.
   203  	app.Seal()
   204  
   205  	return nil
   206  }
   207  
   208  func (app *BaseApp) setMinGasPrices(gasPrices []GasPrice) {
   209  	app.minGasPrices = gasPrices
   210  }
   211  
   212  func (app *BaseApp) setHaltHeight(haltHeight uint64) {
   213  	app.haltHeight = haltHeight
   214  }
   215  
   216  func (app *BaseApp) setHaltTime(haltTime uint64) {
   217  	app.haltTime = haltTime
   218  }
   219  
   220  // Returns a read-only (cache) MultiStore.
   221  // This may be used by keepers for initialization upon restart.
   222  func (app *BaseApp) GetCacheMultiStore() store.MultiStore {
   223  	return app.cms.MultiCacheWrap()
   224  }
   225  
   226  // Router returns the router of the BaseApp.
   227  func (app *BaseApp) Router() Router {
   228  	if app.sealed {
   229  		// We cannot return a router when the app is sealed because we can't have
   230  		// any routes modified which would cause unexpected routing behavior.
   231  		panic("Router() on sealed BaseApp")
   232  	}
   233  	return app.router
   234  }
   235  
   236  // Seal seals a BaseApp. It prohibits any further modifications to a BaseApp.
   237  func (app *BaseApp) Seal() { app.sealed = true }
   238  
   239  // IsSealed returns true if the BaseApp is sealed and false otherwise.
   240  func (app *BaseApp) IsSealed() bool { return app.sealed }
   241  
   242  // setCheckState sets checkState with the cached multistore and
   243  // the context wrapping it.
   244  // It is called by InitChain() and Commit()
   245  func (app *BaseApp) setCheckState(header abci.Header) {
   246  	ms := app.cms.MultiCacheWrap()
   247  	app.checkState = &state{
   248  		ms:  ms,
   249  		ctx: NewContext(RunTxModeCheck, ms, header, app.logger).WithMinGasPrices(app.minGasPrices),
   250  	}
   251  }
   252  
   253  // setDeliverState sets deliverState with the cached multistore and
   254  // the context wrapping it.
   255  // It is called by InitChain() and BeginBlock(),
   256  // and deliverState is set nil on Commit().
   257  func (app *BaseApp) setDeliverState(header abci.Header) {
   258  	ms := app.cms.MultiCacheWrap()
   259  	app.deliverState = &state{
   260  		ms:  ms,
   261  		ctx: NewContext(RunTxModeDeliver, ms, header, app.logger),
   262  	}
   263  }
   264  
   265  // setConsensusParams memoizes the consensus params.
   266  func (app *BaseApp) setConsensusParams(consensusParams *abci.ConsensusParams) {
   267  	app.consensusParams = consensusParams
   268  }
   269  
   270  // setConsensusParams stores the consensus params to the main store.
   271  func (app *BaseApp) storeConsensusParams(consensusParams *abci.ConsensusParams) {
   272  	consensusParamsBz, err := amino.Marshal(consensusParams)
   273  	if err != nil {
   274  		panic(err)
   275  	}
   276  	mainStore := app.cms.GetStore(app.mainKey)
   277  	mainStore.Set(mainConsensusParamsKey, consensusParamsBz)
   278  }
   279  
   280  // getMaximumBlockGas gets the maximum gas from the consensus params. It panics
   281  // if maximum block gas is less than negative one and returns zero if negative
   282  // one.
   283  func (app *BaseApp) getMaximumBlockGas() int64 {
   284  	if app.consensusParams == nil || app.consensusParams.Block == nil {
   285  		return 0
   286  	}
   287  
   288  	maxGas := app.consensusParams.Block.MaxGas
   289  	switch {
   290  	case maxGas < -1:
   291  		panic(fmt.Sprintf("invalid maximum block gas: %d", maxGas))
   292  
   293  	case maxGas == -1:
   294  		return 0
   295  
   296  	default:
   297  		return maxGas
   298  	}
   299  }
   300  
   301  // ----------------------------------------------------------------------------
   302  // ABCI
   303  
   304  // Info implements the ABCI interface.
   305  func (app *BaseApp) Info(req abci.RequestInfo) (res abci.ResponseInfo) {
   306  	lastCommitID := app.cms.LastCommitID()
   307  
   308  	// return res
   309  	res.Data = []byte(app.Name())
   310  	res.LastBlockHeight = lastCommitID.Version
   311  	res.LastBlockAppHash = lastCommitID.Hash
   312  	return
   313  }
   314  
   315  // SetOption implements the ABCI interface.
   316  func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOption) {
   317  	// TODO: Implement!
   318  	return
   319  }
   320  
   321  // InitChain implements the ABCI interface. It runs the initialization logic
   322  // directly on the CommitMultiStore.
   323  func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) {
   324  	// stash the consensus params in the cms main store and memoize
   325  	if req.ConsensusParams != nil {
   326  		app.setConsensusParams(req.ConsensusParams)
   327  		app.storeConsensusParams(req.ConsensusParams)
   328  	}
   329  
   330  	initHeader := &bft.Header{ChainID: req.ChainID, Time: req.Time}
   331  
   332  	// initialize the deliver state and check state with a correct header
   333  	app.setDeliverState(initHeader)
   334  	app.setCheckState(initHeader)
   335  
   336  	if app.initChainer == nil {
   337  		return
   338  	}
   339  
   340  	// add block gas meter for any genesis transactions (allow infinite gas)
   341  	app.deliverState.ctx = app.deliverState.ctx.
   342  		WithBlockGasMeter(store.NewInfiniteGasMeter())
   343  
   344  	res = app.initChainer(app.deliverState.ctx, req)
   345  
   346  	// sanity check
   347  	if len(req.Validators) > 0 {
   348  		if len(req.Validators) != len(res.Validators) {
   349  			panic(fmt.Errorf(
   350  				"len(RequestInitChain.Validators) != len(validators) (%d != %d)",
   351  				len(req.Validators), len(res.Validators)))
   352  		}
   353  		sort.Sort(abci.ValidatorUpdates(req.Validators))
   354  		sort.Sort(abci.ValidatorUpdates(res.Validators))
   355  		for i, val := range res.Validators {
   356  			if !val.Equals(req.Validators[i]) {
   357  				panic(fmt.Errorf("validators[%d] != req.Validators[%d] ", i, i))
   358  			}
   359  		}
   360  	}
   361  
   362  	// NOTE: We don't commit, but BeginBlock for block 1 starts from this
   363  	// deliverState.
   364  	return
   365  }
   366  
   367  // Splits a string path using the delimiter '/'.
   368  // e.g. "this/is/funny" becomes []string{"this", "is", "funny"}
   369  func splitPath(requestPath string) (path []string) {
   370  	path = strings.Split(requestPath, "/")
   371  	// first element is empty string
   372  	if len(path) > 0 && path[0] == "" {
   373  		path = path[1:]
   374  	}
   375  	return path
   376  }
   377  
   378  // Query implements the ABCI interface. It delegates to CommitMultiStore if it
   379  // implements Queryable.
   380  func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
   381  	path := splitPath(req.Path)
   382  	if len(path) == 0 {
   383  		msg := "no query path provided"
   384  		res.Error = ABCIError(std.ErrUnknownRequest(msg))
   385  		return
   386  	}
   387  
   388  	switch path[0] {
   389  	// "/.app", "/.store" prefix for special application queries
   390  	case ".app":
   391  		return handleQueryApp(app, path, req)
   392  
   393  	case ".store":
   394  		return handleQueryStore(app, path, req)
   395  
   396  	// default router queries
   397  	default:
   398  		return handleQueryCustom(app, path, req)
   399  	}
   400  
   401  	msg := "unknown query path " + req.Path
   402  	res.Error = ABCIError(std.ErrUnknownRequest(msg))
   403  	return
   404  }
   405  
   406  func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) {
   407  	if len(path) >= 2 {
   408  		var result Result
   409  
   410  		switch path[1] {
   411  		case "simulate":
   412  			txBytes := req.Data
   413  			var tx Tx
   414  			err := amino.Unmarshal(txBytes, &tx)
   415  			if err != nil {
   416  				res.Error = ABCIError(std.ErrTxDecode(err.Error()))
   417  			} else {
   418  				result = app.Simulate(txBytes, tx)
   419  			}
   420  			res.Height = req.Height
   421  			res.Value = amino.MustMarshal(result)
   422  			return res
   423  		case "version":
   424  			res.Height = req.Height
   425  			res.Value = []byte(app.appVersion)
   426  			return res
   427  		default:
   428  			res.Error = ABCIError(std.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)))
   429  			return
   430  		}
   431  	} else {
   432  		res.Error = ABCIError(std.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)))
   433  		return
   434  	}
   435  }
   436  
   437  func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) {
   438  	// "/store" prefix for store queries
   439  	queryable, ok := app.cms.(store.Queryable)
   440  	if !ok {
   441  		msg := "multistore doesn't support queries"
   442  		res.Error = ABCIError(std.ErrUnknownRequest(msg))
   443  		return
   444  	}
   445  
   446  	req.Path = "/" + strings.Join(path[1:], "/")
   447  
   448  	// when a client did not provide a query height, manually inject the latest
   449  	if req.Height == 0 {
   450  		req.Height = app.LastBlockHeight()
   451  	}
   452  
   453  	if req.Height <= 1 && req.Prove {
   454  		res.Error = ABCIError(std.ErrInternal("cannot query with proof when height <= 1; please provide a valid height"))
   455  		return
   456  	}
   457  
   458  	resp := queryable.Query(req)
   459  	resp.Height = req.Height
   460  	return resp
   461  }
   462  
   463  func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) {
   464  	if len(path) < 1 || path[0] == "" {
   465  		res.Error = ABCIError(std.ErrUnknownRequest("No route for custom query specified"))
   466  		return
   467  	}
   468  
   469  	handler := app.router.Route(path[0])
   470  	if handler == nil {
   471  		res.Error = ABCIError(std.ErrUnknownRequest(fmt.Sprintf("no custom handler found for route %s", path[0])))
   472  		return
   473  	}
   474  
   475  	// when a client did not provide a query height, manually inject the latest
   476  	if req.Height == 0 {
   477  		req.Height = app.LastBlockHeight()
   478  	}
   479  
   480  	if req.Height <= 1 && req.Prove {
   481  		res.Error = ABCIError(std.ErrInternal("cannot query with proof when height <= 1; please provide a valid height"))
   482  		return
   483  	}
   484  
   485  	cacheMS, err := app.cms.MultiImmutableCacheWrapWithVersion(req.Height)
   486  	if err != nil {
   487  		res.Error = ABCIError(std.ErrInternal(
   488  			fmt.Sprintf(
   489  				"failed to load state at height %d; %s (latest height: %d)",
   490  				req.Height, err, app.LastBlockHeight(),
   491  			),
   492  		))
   493  		return
   494  	}
   495  
   496  	// cache wrap the commit-multistore for safety
   497  	// XXX RunTxModeQuery?
   498  	ctx := NewContext(RunTxModeCheck, cacheMS, app.checkState.ctx.BlockHeader(), app.logger).WithMinGasPrices(app.minGasPrices)
   499  
   500  	// Passes the query to the handler.
   501  	res = handler.Query(ctx, req)
   502  	return
   503  }
   504  
   505  func (app *BaseApp) validateHeight(req abci.RequestBeginBlock) error {
   506  	if req.Header.GetHeight() < 1 {
   507  		return fmt.Errorf("invalid height: %d", req.Header.GetHeight())
   508  	}
   509  
   510  	prevHeight := app.LastBlockHeight()
   511  	if req.Header.GetHeight() != prevHeight+1 {
   512  		return fmt.Errorf("invalid height: %d; expected: %d", req.Header.GetHeight(), prevHeight+1)
   513  	}
   514  
   515  	return nil
   516  }
   517  
   518  // BeginBlock implements the ABCI application interface.
   519  func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) {
   520  	if err := app.validateHeight(req); err != nil {
   521  		panic(err)
   522  	}
   523  
   524  	// Initialize the DeliverTx state. If this is the first block, it should
   525  	// already be initialized in InitChain. Otherwise app.deliverState will be
   526  	// nil, since it is reset on Commit.
   527  	if app.deliverState == nil {
   528  		app.setDeliverState(req.Header)
   529  	} else {
   530  		// In the first block, app.deliverState.ctx will already be initialized
   531  		// by InitChain. Context is now updated with Header information.
   532  		app.deliverState.ctx = app.deliverState.ctx.
   533  			WithBlockHeader(req.Header)
   534  	}
   535  
   536  	// add block gas meter
   537  	var gasMeter store.GasMeter
   538  	if maxGas := app.getMaximumBlockGas(); maxGas > 0 {
   539  		gasMeter = store.NewGasMeter(maxGas)
   540  	} else {
   541  		gasMeter = store.NewInfiniteGasMeter()
   542  	}
   543  
   544  	app.deliverState.ctx = app.deliverState.ctx.WithBlockGasMeter(gasMeter)
   545  
   546  	if app.beginBlocker != nil {
   547  		res = app.beginBlocker(app.deliverState.ctx, req)
   548  	}
   549  
   550  	// set the signed validators for addition to context in deliverTx
   551  	if req.LastCommitInfo != nil {
   552  		app.voteInfos = req.LastCommitInfo.Votes
   553  	}
   554  	return
   555  }
   556  
   557  // CheckTx implements the ABCI interface. It runs the "basic checks" to see
   558  // whether or not a transaction can possibly be executed, first decoding and then
   559  // the ante handler (which checks signatures/fees/ValidateBasic).
   560  //
   561  // NOTE:CheckTx does not run the actual Msg handler function(s).
   562  func (app *BaseApp) CheckTx(req abci.RequestCheckTx) (res abci.ResponseCheckTx) {
   563  	var tx Tx
   564  	err := amino.Unmarshal(req.Tx, &tx)
   565  	if err != nil {
   566  		res.Error = ABCIError(std.ErrTxDecode(err.Error()))
   567  		return
   568  	} else {
   569  		result := app.runTx(RunTxModeCheck, req.Tx, tx)
   570  		res.ResponseBase = result.ResponseBase
   571  		res.GasWanted = result.GasWanted
   572  		res.GasUsed = result.GasUsed
   573  		return
   574  	}
   575  }
   576  
   577  // DeliverTx implements the ABCI interface.
   578  func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) (res abci.ResponseDeliverTx) {
   579  	var tx Tx
   580  	err := amino.Unmarshal(req.Tx, &tx)
   581  	if err != nil {
   582  		res.Error = ABCIError(std.ErrTxDecode(err.Error()))
   583  		return
   584  	} else {
   585  		result := app.runTx(RunTxModeDeliver, req.Tx, tx)
   586  		res.ResponseBase = result.ResponseBase
   587  		res.GasWanted = result.GasWanted
   588  		res.GasUsed = result.GasUsed
   589  		return
   590  	}
   591  }
   592  
   593  // validateBasicTxMsgs executes basic validator calls for messages.
   594  func validateBasicTxMsgs(msgs []Msg) error {
   595  	if msgs == nil || len(msgs) == 0 {
   596  		return std.ErrUnknownRequest("Tx.GetMsgs() must return at least one message in list")
   597  	}
   598  
   599  	for _, msg := range msgs {
   600  		// Validate the Msg.
   601  		err := msg.ValidateBasic()
   602  		if err != nil {
   603  			return err
   604  		}
   605  	}
   606  
   607  	return nil
   608  }
   609  
   610  // retrieve the context for the tx w/ txBytes and other memoized values.
   611  func (app *BaseApp) getContextForTx(mode RunTxMode, txBytes []byte) (ctx Context) {
   612  	ctx = app.getState(mode).ctx.
   613  		WithMode(mode).
   614  		WithTxBytes(txBytes).
   615  		WithVoteInfos(app.voteInfos).
   616  		WithConsensusParams(app.consensusParams)
   617  
   618  	if mode == RunTxModeSimulate {
   619  		ctx, _ = ctx.CacheContext()
   620  	}
   621  
   622  	return
   623  }
   624  
   625  // / runMsgs iterates through all the messages and executes them.
   626  func (app *BaseApp) runMsgs(ctx Context, msgs []Msg, mode RunTxMode) (result Result) {
   627  	msgLogs := make([]string, 0, len(msgs))
   628  
   629  	data := make([]byte, 0, len(msgs))
   630  	err := error(nil)
   631  	events := []Event{}
   632  
   633  	// NOTE: GasWanted is determined by ante handler and GasUsed by the GasMeter.
   634  	for i, msg := range msgs {
   635  		// match message route
   636  		msgRoute := msg.Route()
   637  		handler := app.router.Route(msgRoute)
   638  		if handler == nil {
   639  			result.Error = ABCIError(std.ErrUnknownRequest("unrecognized message type: " + msgRoute))
   640  			return
   641  		}
   642  
   643  		var msgResult Result
   644  		ctx = ctx.WithEventLogger(NewEventLogger())
   645  
   646  		// run the message!
   647  		// skip actual execution for CheckTx mode
   648  		if mode != RunTxModeCheck {
   649  			msgResult = handler.Process(ctx, msg)
   650  		}
   651  
   652  		// Each message result's Data must be length prefixed in order to separate
   653  		// each result.
   654  		data = append(data, msgResult.Data...)
   655  		events = append(events, msgResult.Events...)
   656  		defer func() {
   657  			events = append(events, ctx.EventLogger().Events()...)
   658  			result.Events = events
   659  		}()
   660  		// TODO append msgevent from ctx. XXX XXX
   661  
   662  		// stop execution and return on first failed message
   663  		if !msgResult.IsOK() {
   664  			msgLogs = append(msgLogs,
   665  				fmt.Sprintf("msg:%d,success:%v,log:%s,events:%v",
   666  					i, false, msgResult.Log, events))
   667  			err = msgResult.Error
   668  			break
   669  		}
   670  
   671  		msgLogs = append(msgLogs,
   672  			fmt.Sprintf("msg:%d,success:%v,log:%s,events:%v",
   673  				i, true, msgResult.Log, events))
   674  	}
   675  
   676  	result.Error = ABCIError(err)
   677  	result.Data = data
   678  	result.Log = strings.Join(msgLogs, "\n")
   679  	result.GasUsed = ctx.GasMeter().GasConsumed()
   680  	result.Events = events
   681  	return result
   682  }
   683  
   684  // Returns the applications's deliverState if app is in RunTxModeDeliver,
   685  // otherwise it returns the application's checkstate.
   686  func (app *BaseApp) getState(mode RunTxMode) *state {
   687  	if mode == RunTxModeCheck || mode == RunTxModeSimulate {
   688  		return app.checkState
   689  	}
   690  
   691  	return app.deliverState
   692  }
   693  
   694  // cacheTxContext returns a new context based off of the provided context with
   695  // a cache wrapped multi-store.
   696  func (app *BaseApp) cacheTxContext(ctx Context, txBytes []byte) (
   697  	Context, store.MultiStore,
   698  ) {
   699  	ms := ctx.MultiStore()
   700  	// TODO: https://github.com/tendermint/classic/sdk/issues/2824
   701  	msCache := ms.MultiCacheWrap()
   702  	return ctx.WithMultiStore(msCache), msCache
   703  }
   704  
   705  // runTx processes a transaction. The transactions is processed via an
   706  // anteHandler. The provided txBytes may be nil in some cases, eg. in tests. For
   707  // further details on transaction execution, reference the BaseApp SDK
   708  // documentation.
   709  func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx Tx) (result Result) {
   710  	// NOTE: GasWanted should be returned by the AnteHandler. GasUsed is
   711  	// determined by the GasMeter. We need access to the context to get the gas
   712  	// meter so we initialize upfront.
   713  	var gasWanted int64
   714  
   715  	ctx := app.getContextForTx(mode, txBytes)
   716  	ms := ctx.MultiStore()
   717  	if mode == RunTxModeDeliver {
   718  		gasleft := ctx.BlockGasMeter().Remaining()
   719  		ctx = ctx.WithGasMeter(store.NewPassthroughGasMeter(
   720  			ctx.GasMeter(),
   721  			gasleft,
   722  		))
   723  	}
   724  
   725  	// only run the tx if there is block gas remaining
   726  	if mode == RunTxModeDeliver && ctx.BlockGasMeter().IsOutOfGas() {
   727  		result.Error = ABCIError(std.ErrOutOfGas("no block gas left to run tx"))
   728  		return
   729  	}
   730  
   731  	var startingGas int64
   732  	if mode == RunTxModeDeliver {
   733  		startingGas = ctx.BlockGasMeter().GasConsumed()
   734  	}
   735  
   736  	defer func() {
   737  		if r := recover(); r != nil {
   738  			switch ex := r.(type) {
   739  			case store.OutOfGasException:
   740  				log := fmt.Sprintf(
   741  					"out of gas, gasWanted: %d, gasUsed: %d location: %v",
   742  					gasWanted,
   743  					ctx.GasMeter().GasConsumed(),
   744  					ex.Descriptor,
   745  				)
   746  				result.Error = ABCIError(std.ErrOutOfGas(log))
   747  				result.Log = log
   748  				result.GasWanted = gasWanted
   749  				result.GasUsed = ctx.GasMeter().GasConsumed()
   750  				return
   751  			default:
   752  				log := fmt.Sprintf("recovered: %v\nstack:\n%v", r, string(debug.Stack()))
   753  				result.Error = ABCIError(std.ErrInternal(log))
   754  				result.Log = log
   755  				result.GasWanted = gasWanted
   756  				result.GasUsed = ctx.GasMeter().GasConsumed()
   757  				return
   758  			}
   759  		}
   760  		// Whether AnteHandler panics or not.
   761  		result.GasWanted = gasWanted
   762  		result.GasUsed = ctx.GasMeter().GasConsumed()
   763  	}()
   764  
   765  	// If BlockGasMeter() panics it will be caught by the above recover and will
   766  	// return an error - in any case BlockGasMeter will consume gas past the limit.
   767  	//
   768  	// NOTE: This must exist in a separate defer function for the above recovery
   769  	// to recover from this one.
   770  	defer func() {
   771  		if mode == RunTxModeDeliver {
   772  			ctx.BlockGasMeter().ConsumeGas(
   773  				ctx.GasMeter().GasConsumedToLimit(),
   774  				"block gas meter",
   775  			)
   776  
   777  			if ctx.BlockGasMeter().GasConsumed() < startingGas {
   778  				panic(std.ErrGasOverflow("tx gas summation"))
   779  			}
   780  		}
   781  	}()
   782  
   783  	msgs := tx.GetMsgs()
   784  	if err := validateBasicTxMsgs(msgs); err != nil {
   785  		result.Error = ABCIError(err)
   786  		return
   787  	}
   788  
   789  	if app.anteHandler != nil {
   790  		var anteCtx Context
   791  		var msCache store.MultiStore
   792  
   793  		// Cache wrap context before anteHandler call in case
   794  		// it aborts.  This is required for both CheckTx and
   795  		// DeliverTx.  Ref:
   796  		// https://github.com/tendermint/classic/sdk/issues/2772
   797  		//
   798  		// NOTE: Alternatively, we could require that
   799  		// anteHandler ensures that writes do not happen if
   800  		// aborted/failed.  This may have some performance
   801  		// benefits, but it'll be more difficult to get
   802  		// right.
   803  		anteCtx, msCache = app.cacheTxContext(ctx, txBytes)
   804  		// Call AnteHandler.
   805  		// NOTE: It is the responsibility of the anteHandler
   806  		// to use something like passthroughGasMeter to
   807  		// account for ante handler gas usage, despite
   808  		// OutOfGasExceptions.
   809  		newCtx, result, abort := app.anteHandler(anteCtx, tx, mode == RunTxModeSimulate)
   810  		if newCtx.IsZero() {
   811  			panic("newCtx must not be zero")
   812  		}
   813  		if abort && result.Error == nil {
   814  			panic("result.Error should be set for abort")
   815  		}
   816  		if abort {
   817  			// NOTE: first we must set ctx above,
   818  			// because a previous defer call sets
   819  			// result.GasUsed, regardless of error.
   820  			return result
   821  		} else {
   822  			// Revert cache wrapping of multistore.
   823  			ctx = newCtx.WithMultiStore(ms)
   824  			msCache.MultiWrite()
   825  			gasWanted = result.GasWanted
   826  		}
   827  	}
   828  
   829  	// Create a new context based off of the existing context with a cache wrapped
   830  	// multi-store in case message processing fails.
   831  	runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes)
   832  	result = app.runMsgs(runMsgCtx, msgs, mode)
   833  	result.GasWanted = gasWanted
   834  
   835  	// Safety check: don't write the cache state unless we're in DeliverTx.
   836  	if mode != RunTxModeDeliver {
   837  		return result
   838  	}
   839  
   840  	// only update state if all messages pass
   841  	if result.IsOK() {
   842  		msCache.MultiWrite()
   843  	}
   844  
   845  	return result
   846  }
   847  
   848  // EndBlock implements the ABCI interface.
   849  func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
   850  	if app.endBlocker != nil {
   851  		res = app.endBlocker(app.deliverState.ctx, req)
   852  	}
   853  
   854  	return
   855  }
   856  
   857  // Commit implements the ABCI interface. It will commit all state that exists in
   858  // the deliver state's multi-store and includes the resulting commit ID in the
   859  // returned abci.ResponseCommit. Commit will set the check state based on the
   860  // latest header and reset the deliver state. Also, if a non-zero halt height is
   861  // defined in config, Commit will execute a deferred function call to check
   862  // against that height and gracefully halt if it matches the latest committed
   863  // height.
   864  func (app *BaseApp) Commit() (res abci.ResponseCommit) {
   865  	header := app.deliverState.ctx.BlockHeader()
   866  
   867  	var halt bool
   868  
   869  	switch {
   870  	case app.haltHeight > 0 && uint64(header.GetHeight()) >= app.haltHeight:
   871  		halt = true
   872  
   873  	case app.haltTime > 0 && header.GetTime().Unix() >= int64(app.haltTime):
   874  		halt = true
   875  	}
   876  
   877  	if halt {
   878  		app.halt()
   879  
   880  		// Note: State is not actually committed when halted. Logs from Tendermint
   881  		// can be ignored.
   882  		return abci.ResponseCommit{}
   883  	}
   884  
   885  	// Write the DeliverTx state which is cache-wrapped and commit the MultiStore.
   886  	// The write to the DeliverTx state writes all state transitions to the root
   887  	// MultiStore (app.cms) so when Commit() is called is persists those values.
   888  	app.deliverState.ms.MultiWrite()
   889  	commitID := app.cms.Commit()
   890  	app.logger.Debug("Commit synced", "commit", fmt.Sprintf("%X", commitID))
   891  
   892  	// Save this header.
   893  	baseStore := app.cms.GetStore(app.baseKey)
   894  	if baseStore == nil {
   895  		res.Error = ABCIError(errors.New("baseapp expects MultiStore with 'base' Store"))
   896  		return
   897  	}
   898  	headerBz := amino.MustMarshal(header)
   899  	baseStore.Set(mainLastHeaderKey, headerBz)
   900  
   901  	// Reset the Check state to the latest committed.
   902  	//
   903  	// NOTE: This is safe because Tendermint holds a lock on the mempool for
   904  	// Commit. Use the header from this latest block.
   905  	app.setCheckState(header)
   906  
   907  	// empty/reset the deliver state
   908  	app.deliverState = nil
   909  
   910  	// return.
   911  	res.Data = commitID.Hash
   912  	return
   913  }
   914  
   915  // halt attempts to gracefully shutdown the node via SIGINT and SIGTERM falling
   916  // back on os.Exit if both fail.
   917  func (app *BaseApp) halt() {
   918  	app.logger.Info("halting node per configuration", "height", app.haltHeight, "time", app.haltTime)
   919  
   920  	p, err := os.FindProcess(os.Getpid())
   921  	if err == nil {
   922  		// attempt cascading signals in case SIGINT fails (os dependent)
   923  		sigIntErr := p.Signal(syscall.SIGINT)
   924  		sigTermErr := p.Signal(syscall.SIGTERM)
   925  
   926  		if sigIntErr == nil || sigTermErr == nil {
   927  			return
   928  		}
   929  	}
   930  
   931  	// Resort to exiting immediately if the process could not be found or killed
   932  	// via SIGINT/SIGTERM signals.
   933  	app.logger.Info("failed to send SIGINT/SIGTERM; exiting...")
   934  	os.Exit(0)
   935  }
   936  
   937  // TODO implement cleanup
   938  func (app *BaseApp) Close() error {
   939  	return nil // XXX
   940  }
   941  
   942  // ----------------------------------------------------------------------------
   943  // State
   944  
   945  type state struct {
   946  	ms  store.MultiStore
   947  	ctx Context
   948  }
   949  
   950  func (st *state) MultiCacheWrap() store.MultiStore {
   951  	return st.ms.MultiCacheWrap()
   952  }
   953  
   954  func (st *state) Context() Context {
   955  	return st.ctx
   956  }