Skip to content

Commit 8f8e43a

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 f665371 commit 8f8e43a

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ Ref: https://keepachangelog.com/en/1.0.0/
3636

3737
# Changelog
3838

39+
## Unreleased
40+
41+
### Bug Fixes
42+
43+
* (baseapp) [#25529](https://github.com/cosmos/cosmos-sdk/pull/25529) isolate CheckTx and simulation state from DeliverTx commits by loading the last committed snapshot and keeping the simulation context in sync.
44+
3945
## [v0.53.4](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.53.3) - 2025-07-25
4046

4147
This patch update also includes minor dependency bumps.

baseapp/state/manager.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ type Manager struct {
2424
// - checkState: Used for CheckTx, which is set based on the previous block's
2525
// state. This state is never committed.
2626
//
27+
// - simulationState: Mirrors the last committed state for simulations. It shares
28+
// the same root snapshot as CheckTx but is never written back.
29+
//
2730
// - prepareProposalState: Used for PrepareProposal, which is set based on the
2831
// previous block's state. This state is never committed. In case of multiple
2932
// consensus rounds, the state is always reset to the previous block's state.
@@ -35,6 +38,7 @@ type Manager struct {
3538
// - finalizeBlockState: Used for FinalizeBlock, which is set based on the
3639
// previous block's state. This state is committed.
3740
checkState *State
41+
simulationState *State
3842
prepareProposalState *State
3943
processProposalState *State
4044
finalizeBlockState *State
@@ -63,6 +67,15 @@ func (mgr *Manager) GetState(mode sdk.ExecMode) *State {
6367
case sdk.ExecModeProcessProposal:
6468
return mgr.processProposalState
6569

70+
case sdk.ExecModeSimulate:
71+
// Keep the simulation context aligned with the CheckTx context while
72+
// preserving its own store branch.
73+
if mgr.checkState != nil && mgr.simulationState != nil {
74+
mgr.simulationState.SetContext(mgr.checkState.Context().WithMultiStore(mgr.simulationState.MultiStore))
75+
}
76+
77+
return mgr.simulationState
78+
6679
default:
6780
return mgr.checkState
6881
}
@@ -79,6 +92,20 @@ func (mgr *Manager) SetState(
7992
streamingManager storetypes.StreamingManager,
8093
) {
8194
ms := unbranchedStore.CacheMultiStore()
95+
if mode == sdk.ExecModeCheck {
96+
// Load the last committed version so CheckTx (and by extension simulations)
97+
// operate on the same state that DeliverTx committed in the previous block.
98+
// Ref: https://github.com/cosmos/cosmos-sdk/issues/20685
99+
//
100+
// Using the versioned cache also unwraps any inter-block cache layers,
101+
// preventing simulation runs from polluting the global inter-block cache
102+
// with transient writes.
103+
// Ref: https://github.com/cosmos/cosmos-sdk/issues/23891
104+
if versionedCache, err := unbranchedStore.CacheMultiStoreWithVersion(h.Height); err == nil {
105+
ms = versionedCache
106+
}
107+
}
108+
82109
headerInfo := header.Info{
83110
Height: h.Height,
84111
Time: h.Time,
@@ -97,8 +124,14 @@ func (mgr *Manager) SetState(
97124

98125
switch mode {
99126
case sdk.ExecModeCheck:
100-
baseState.SetContext(baseState.Context().WithIsCheckTx(true).WithMinGasPrices(mgr.gasConfig.MinGasPrices))
101-
mgr.checkState = baseState
127+
// Simulations never persist state, so they can reuse the base snapshot
128+
// that was branched off the last committed height.
129+
mgr.simulationState = baseState
130+
131+
// Branch again for CheckTx so AnteHandler writes do not leak back into
132+
// the shared simulation snapshot.
133+
checkMs := ms.CacheMultiStore()
134+
mgr.checkState = NewState(baseState.Context().WithIsCheckTx(true).WithMinGasPrices(mgr.gasConfig.MinGasPrices).WithMultiStore(checkMs), checkMs)
102135

103136
case sdk.ExecModePrepareProposal:
104137
mgr.prepareProposalState = baseState
@@ -121,6 +154,7 @@ func (mgr *Manager) ClearState(mode sdk.ExecMode) {
121154
switch mode {
122155
case sdk.ExecModeCheck:
123156
mgr.checkState = nil
157+
mgr.simulationState = nil
124158

125159
case sdk.ExecModePrepareProposal:
126160
mgr.prepareProposalState = nil

0 commit comments

Comments
 (0)