Skip to content

Commit 7ba72a6

Browse files
committed
fix: isolate CheckTx and simulation state from DeliverTx commits by loading the last committed snapshot and keeping the simulation context in sync
1 parent 8ffc807 commit 7ba72a6

File tree

2 files changed

+33
-2
lines changed

2 files changed

+33
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
4242

4343
* (baseapp) [#20107](https://github.com/cosmos/cosmos-sdk/pull/20107) Avoid header height overwrite block height.
4444
* (cli) [#20020](https://github.com/cosmos/cosmos-sdk/pull/20020) Make bootstrap-state command support both new and legacy genesis format.
45+
* (baseapp) [#25530](https://github.com/cosmos/cosmos-sdk/pull/25530) isolate CheckTx and simulation state from DeliverTx commits by loading the last committed snapshot and keeping the simulation context in sync.
4546

4647
## [v0.50.6](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.6) - 2024-04-22
4748

baseapp/baseapp.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ type BaseApp struct {
104104
// - checkState: Used for CheckTx, which is set based on the previous block's
105105
// state. This state is never committed.
106106
//
107+
// - simulationState: Mirrors the last committed state for simulations. It shares
108+
// the same root snapshot as CheckTx but is never written back.
109+
//
107110
// - prepareProposalState: Used for PrepareProposal, which is set based on the
108111
// previous block's state. This state is never committed. In case of multiple
109112
// consensus rounds, the state is always reset to the previous block's state.
@@ -115,6 +118,7 @@ type BaseApp struct {
115118
// - finalizeBlockState: Used for FinalizeBlock, which is set based on the
116119
// previous block's state. This state is committed.
117120
checkState *state
121+
simulationState *state
118122
prepareProposalState *state
119123
processProposalState *state
120124
finalizeBlockState *state
@@ -478,6 +482,19 @@ func (app *BaseApp) IsSealed() bool { return app.sealed }
478482
// multi-store branch, and provided header.
479483
func (app *BaseApp) setState(mode execMode, h cmtproto.Header) {
480484
ms := app.cms.CacheMultiStore()
485+
if mode == execModeCheck {
486+
// Load the last committed version so CheckTx (and by extension simulations)
487+
// operate on the same state that DeliverTx committed in the previous block.
488+
// Ref: https://github.com/cosmos/cosmos-sdk/issues/20685
489+
//
490+
// Using the versioned cache also unwraps any inter-block cache layers,
491+
// preventing simulation runs from polluting the global inter-block cache
492+
// with transient writes.
493+
// Ref: https://github.com/cosmos/cosmos-sdk/issues/23891
494+
if versionedCache, err := app.cms.CacheMultiStoreWithVersion(h.Height); err == nil {
495+
ms = versionedCache
496+
}
497+
}
481498
headerInfo := header.Info{
482499
Height: h.Height,
483500
Time: h.Time,
@@ -493,8 +510,14 @@ func (app *BaseApp) setState(mode execMode, h cmtproto.Header) {
493510

494511
switch mode {
495512
case execModeCheck:
496-
baseState.SetContext(baseState.Context().WithIsCheckTx(true).WithMinGasPrices(app.minGasPrices))
497-
app.checkState = baseState
513+
// Simulations never persist state, so they can reuse the base snapshot
514+
// that was branched off the last committed height.
515+
app.simulationState = baseState
516+
517+
// Branch again for CheckTx so AnteHandler writes do not leak back into
518+
// the shared simulation snapshot.
519+
checkMs := ms.CacheMultiStore()
520+
app.checkState = &state{ctx: baseState.Context().WithIsCheckTx(true).WithMinGasPrices(app.minGasPrices).WithMultiStore(checkMs), ms: checkMs}
498521

499522
case execModePrepareProposal:
500523
app.prepareProposalState = baseState
@@ -640,7 +663,14 @@ func (app *BaseApp) getState(mode execMode) *state {
640663

641664
case execModeProcessProposal:
642665
return app.processProposalState
666+
case execModeSimulate:
667+
// Keep the simulation context aligned with the CheckTx context while
668+
// preserving its own store branch.
669+
if app.checkState != nil && app.simulationState != nil {
670+
app.simulationState.SetContext(app.checkState.Context().WithMultiStore(app.simulationState.ms))
671+
}
643672

673+
return app.simulationState
644674
default:
645675
return app.checkState
646676
}

0 commit comments

Comments
 (0)