package types import ( "context" "time" abci "github.com/cometbft/cometbft/abci/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" "cosmossdk.io/core/comet" "cosmossdk.io/core/header" "cosmossdk.io/log" "cosmossdk.io/store/gaskv" storetypes "cosmossdk.io/store/types" ) // ExecMode defines the execution mode which can be set on a Context. type ExecMode uint8 // All possible execution modes. const ( ExecModeCheck ExecMode = iota ExecModeReCheck ExecModeSimulate ExecModePrepareProposal ExecModeProcessProposal ExecModeVoteExtension ExecModeVerifyVoteExtension ExecModeFinalize ) /* Context is an immutable object contains all information needed to process a request. It contains a context.Context object inside if you want to use that, but please do not over-use it. We try to keep all data structured and standard additions here would be better just to add to the Context struct */ type Context struct { baseCtx context.Context ms storetypes.MultiStore // Deprecated: Use HeaderService for height, time, and chainID and CometService for the rest header cmtproto.Header // Deprecated: Use HeaderService for hash headerHash []byte // Deprecated: Use HeaderService for chainID and CometService for the rest chainID string txBytes []byte logger log.Logger voteInfo []abci.VoteInfo gasMeter storetypes.GasMeter blockGasMeter storetypes.GasMeter checkTx bool recheckTx bool // if recheckTx == true, then checkTx must also be true sigverifyTx bool // when run simulation, because the private key corresponding to the account in the genesis.json randomly generated, we must skip the sigverify. execMode ExecMode minGasPrice DecCoins consParams cmtproto.ConsensusParams eventManager EventManagerI priority int64 // The tx priority, only relevant in CheckTx kvGasConfig storetypes.GasConfig transientKVGasConfig storetypes.GasConfig streamingManager storetypes.StreamingManager cometInfo comet.BlockInfo headerInfo header.Info } // Proposed rename, not done to avoid API breakage type Request = Context // Read-only accessors func (c Context) Context() context.Context { return c.baseCtx } func (c Context) MultiStore() storetypes.MultiStore { return c.ms } func (c Context) BlockHeight() int64 { return c.header.Height } func (c Context) BlockTime() time.Time { return c.header.Time } func (c Context) ChainID() string { return c.chainID } func (c Context) TxBytes() []byte { return c.txBytes } func (c Context) Logger() log.Logger { return c.logger } func (c Context) VoteInfos() []abci.VoteInfo { return c.voteInfo } func (c Context) GasMeter() storetypes.GasMeter { return c.gasMeter } func (c Context) BlockGasMeter() storetypes.GasMeter { return c.blockGasMeter } func (c Context) IsCheckTx() bool { return c.checkTx } func (c Context) IsReCheckTx() bool { return c.recheckTx } func (c Context) IsSigverifyTx() bool { return c.sigverifyTx } func (c Context) ExecMode() ExecMode { return c.execMode } func (c Context) MinGasPrices() DecCoins { return c.minGasPrice } func (c Context) EventManager() EventManagerI { return c.eventManager } func (c Context) Priority() int64 { return c.priority } func (c Context) KVGasConfig() storetypes.GasConfig { return c.kvGasConfig } func (c Context) TransientKVGasConfig() storetypes.GasConfig { return c.transientKVGasConfig } func (c Context) StreamingManager() storetypes.StreamingManager { return c.streamingManager } func (c Context) CometInfo() comet.BlockInfo { return c.cometInfo } func (c Context) HeaderInfo() header.Info { return c.headerInfo } // BlockHeader returns the header by value. func (c Context) BlockHeader() cmtproto.Header { return c.header } // HeaderHash returns a copy of the header hash obtained during abci.RequestBeginBlock func (c Context) HeaderHash() []byte { hash := make([]byte, len(c.headerHash)) copy(hash, c.headerHash) return hash } func (c Context) ConsensusParams() cmtproto.ConsensusParams { return c.consParams } func (c Context) Deadline() (deadline time.Time, ok bool) { return c.baseCtx.Deadline() } func (c Context) Done() <-chan struct{} { return c.baseCtx.Done() } func (c Context) Err() error { return c.baseCtx.Err() } // create a new context func NewContext(ms storetypes.MultiStore, header cmtproto.Header, isCheckTx bool, logger log.Logger) Context { // https://github.com/gogo/protobuf/issues/519 header.Time = header.Time.UTC() return Context{ baseCtx: context.Background(), ms: ms, header: header, chainID: header.ChainID, checkTx: isCheckTx, sigverifyTx: true, logger: logger, gasMeter: storetypes.NewInfiniteGasMeter(), minGasPrice: DecCoins{}, eventManager: NewEventManager(), kvGasConfig: storetypes.KVGasConfig(), transientKVGasConfig: storetypes.TransientGasConfig(), } } // WithContext returns a Context with an updated context.Context. func (c Context) WithContext(ctx context.Context) Context { c.baseCtx = ctx return c } // WithMultiStore returns a Context with an updated MultiStore. func (c Context) WithMultiStore(ms storetypes.MultiStore) Context { c.ms = ms return c } // WithBlockHeader returns a Context with an updated CometBFT block header in UTC time. func (c Context) WithBlockHeader(header cmtproto.Header) Context { // https://github.com/gogo/protobuf/issues/519 header.Time = header.Time.UTC() c.header = header return c } // WithHeaderHash returns a Context with an updated CometBFT block header hash. func (c Context) WithHeaderHash(hash []byte) Context { temp := make([]byte, len(hash)) copy(temp, hash) c.headerHash = temp return c } // WithBlockTime returns a Context with an updated CometBFT block header time in UTC with no monotonic component. // Stripping the monotonic component is for time equality. func (c Context) WithBlockTime(newTime time.Time) Context { newHeader := c.BlockHeader() // https://github.com/gogo/protobuf/issues/519 newHeader.Time = newTime.Round(0).UTC() return c.WithBlockHeader(newHeader) } // WithProposer returns a Context with an updated proposer consensus address. func (c Context) WithProposer(addr ConsAddress) Context { newHeader := c.BlockHeader() newHeader.ProposerAddress = addr.Bytes() return c.WithBlockHeader(newHeader) } // WithBlockHeight returns a Context with an updated block height. func (c Context) WithBlockHeight(height int64) Context { newHeader := c.BlockHeader() newHeader.Height = height return c.WithBlockHeader(newHeader) } // WithChainID returns a Context with an updated chain identifier. func (c Context) WithChainID(chainID string) Context { c.chainID = chainID return c } // WithTxBytes returns a Context with an updated txBytes. func (c Context) WithTxBytes(txBytes []byte) Context { c.txBytes = txBytes return c } // WithLogger returns a Context with an updated logger. func (c Context) WithLogger(logger log.Logger) Context { c.logger = logger return c } // WithVoteInfos returns a Context with an updated consensus VoteInfo. func (c Context) WithVoteInfos(voteInfo []abci.VoteInfo) Context { c.voteInfo = voteInfo return c } // WithGasMeter returns a Context with an updated transaction GasMeter. func (c Context) WithGasMeter(meter storetypes.GasMeter) Context { c.gasMeter = meter return c } // WithBlockGasMeter returns a Context with an updated block GasMeter func (c Context) WithBlockGasMeter(meter storetypes.GasMeter) Context { c.blockGasMeter = meter return c } // WithKVGasConfig returns a Context with an updated gas configuration for // the KVStore func (c Context) WithKVGasConfig(gasConfig storetypes.GasConfig) Context { c.kvGasConfig = gasConfig return c } // WithTransientKVGasConfig returns a Context with an updated gas configuration for // the transient KVStore func (c Context) WithTransientKVGasConfig(gasConfig storetypes.GasConfig) Context { c.transientKVGasConfig = gasConfig return c } // WithIsCheckTx enables or disables CheckTx value for verifying transactions and returns an updated Context func (c Context) WithIsCheckTx(isCheckTx bool) Context { c.checkTx = isCheckTx c.execMode = ExecModeCheck return c } // WithIsRecheckTx called with true will also set true on checkTx in order to // enforce the invariant that if recheckTx = true then checkTx = true as well. func (c Context) WithIsReCheckTx(isRecheckTx bool) Context { if isRecheckTx { c.checkTx = true } c.recheckTx = isRecheckTx c.execMode = ExecModeReCheck return c } // WithIsSigverifyTx called with true will sigverify in auth module func (c Context) WithIsSigverifyTx(isSigverifyTx bool) Context { c.sigverifyTx = isSigverifyTx return c } // WithExecMode returns a Context with an updated ExecMode. func (c Context) WithExecMode(m ExecMode) Context { c.execMode = m return c } // WithMinGasPrices returns a Context with an updated minimum gas price value func (c Context) WithMinGasPrices(gasPrices DecCoins) Context { c.minGasPrice = gasPrices return c } // WithConsensusParams returns a Context with an updated consensus params func (c Context) WithConsensusParams(params cmtproto.ConsensusParams) Context { c.consParams = params return c } // WithEventManager returns a Context with an updated event manager func (c Context) WithEventManager(em EventManagerI) Context { c.eventManager = em return c } // WithPriority returns a Context with an updated tx priority func (c Context) WithPriority(p int64) Context { c.priority = p return c } // WithStreamingManager returns a Context with an updated streaming manager func (c Context) WithStreamingManager(sm storetypes.StreamingManager) Context { c.streamingManager = sm return c } // WithCometInfo returns a Context with an updated comet info func (c Context) WithCometInfo(cometInfo comet.BlockInfo) Context { c.cometInfo = cometInfo return c } // WithHeaderInfo returns a Context with an updated header info func (c Context) WithHeaderInfo(headerInfo header.Info) Context { // Settime to UTC headerInfo.Time = headerInfo.Time.UTC() c.headerInfo = headerInfo return c } // TODO: remove??? func (c Context) IsZero() bool { return c.ms == nil } func (c Context) WithValue(key, value any) Context { c.baseCtx = context.WithValue(c.baseCtx, key, value) return c } func (c Context) Value(key any) any { if key == SdkContextKey { return c } return c.baseCtx.Value(key) } // ---------------------------------------------------------------------------- // Store / Caching // ---------------------------------------------------------------------------- // KVStore fetches a KVStore from the MultiStore. func (c Context) KVStore(key storetypes.StoreKey) storetypes.KVStore { return gaskv.NewStore(c.ms.GetKVStore(key), c.gasMeter, c.kvGasConfig) } // TransientStore fetches a TransientStore from the MultiStore. func (c Context) TransientStore(key storetypes.StoreKey) storetypes.KVStore { return gaskv.NewStore(c.ms.GetKVStore(key), c.gasMeter, c.transientKVGasConfig) } // CacheContext returns a new Context with the multi-store cached and a new // EventManager. The cached context is written to the context when writeCache // is called. Note, events are automatically emitted on the parent context's // EventManager when the caller executes the write. func (c Context) CacheContext() (cc Context, writeCache func()) { cms := c.ms.CacheMultiStore() cc = c.WithMultiStore(cms).WithEventManager(NewEventManager()) writeCache = func() { c.EventManager().EmitEvents(cc.EventManager().Events()) cms.Write() } return cc, writeCache } var ( _ context.Context = Context{} _ storetypes.Context = Context{} ) // ContextKey defines a type alias for a stdlib Context key. type ContextKey string // SdkContextKey is the key in the context.Context which holds the sdk.Context. const SdkContextKey ContextKey = "sdk-context" // WrapSDKContext returns a stdlib context.Context with the provided sdk.Context's internal // context as a value. It is useful for passing an sdk.Context through methods that take a // stdlib context.Context parameter such as generated gRPC methods. To get the original // sdk.Context back, call UnwrapSDKContext. // // Deprecated: there is no need to wrap anymore as the Cosmos SDK context implements context.Context. func WrapSDKContext(ctx Context) context.Context { return ctx } // UnwrapSDKContext retrieves a Context from a context.Context instance // attached with WrapSDKContext. It panics if a Context was not properly // attached func UnwrapSDKContext(ctx context.Context) Context { if sdkCtx, ok := ctx.(Context); ok { return sdkCtx } return ctx.Value(SdkContextKey).(Context) }