diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index bee423db9..b9addbd99 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -23,3 +23,5 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi ### IMPROVEMENTS ### BUG FIXES + +- [cli] [#7837](https://github.com/tendermint/tendermint/pull/7837) fix app hash in state rollback. (@yihuang) diff --git a/state/rollback.go b/state/rollback.go index 9c30b0efe..388a597b1 100644 --- a/state/rollback.go +++ b/state/rollback.go @@ -43,6 +43,12 @@ func Rollback(bs BlockStore, ss Store) (int64, []byte, error) { if rollbackBlock == nil { return -1, nil, fmt.Errorf("block at height %d not found", rollbackHeight) } + // We also need to retrieve the latest block because the app hash and last + // results hash is only agreed upon in the following block. + latestBlock := bs.LoadBlockMeta(invalidState.LastBlockHeight) + if latestBlock == nil { + return -1, nil, fmt.Errorf("block at height %d not found", invalidState.LastBlockHeight) + } previousLastValidatorSet, err := ss.LoadValidators(rollbackHeight) if err != nil { @@ -91,8 +97,8 @@ func Rollback(bs BlockStore, ss Store) (int64, []byte, error) { ConsensusParams: previousParams, LastHeightConsensusParamsChanged: paramsChangeHeight, - LastResultsHash: rollbackBlock.Header.LastResultsHash, - AppHash: rollbackBlock.Header.AppHash, + LastResultsHash: latestBlock.Header.LastResultsHash, + AppHash: latestBlock.Header.AppHash, } // persist the new state. This overrides the invalid one. NOTE: this will also diff --git a/state/rollback_test.go b/state/rollback_test.go index 9eea4c8b9..3428b0ef8 100644 --- a/state/rollback_test.go +++ b/state/rollback_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/tmhash" tmstate "github.com/tendermint/tendermint/proto/tendermint/state" tmversion "github.com/tendermint/tendermint/proto/tendermint/version" @@ -49,12 +50,22 @@ func TestRollback(t *testing.T) { BlockID: initialState.LastBlockID, Header: types.Header{ Height: initialState.LastBlockHeight, - AppHash: initialState.AppHash, + AppHash: crypto.CRandBytes(tmhash.Size), LastBlockID: makeBlockIDRandom(), LastResultsHash: initialState.LastResultsHash, }, } - blockStore.On("LoadBlockMeta", initialState.LastBlockHeight).Return(block) + nextBlock := &types.BlockMeta{ + BlockID: initialState.LastBlockID, + Header: types.Header{ + Height: nextState.LastBlockHeight, + AppHash: initialState.AppHash, + LastBlockID: block.BlockID, + LastResultsHash: nextState.LastResultsHash, + }, + } + blockStore.On("LoadBlockMeta", height).Return(block) + blockStore.On("LoadBlockMeta", nextHeight).Return(nextBlock) blockStore.On("Height").Return(nextHeight) // rollback the state @@ -84,6 +95,7 @@ func TestRollbackNoBlocks(t *testing.T) { stateStore := setupStateStore(t, height) blockStore := &mocks.BlockStore{} blockStore.On("Height").Return(height) + blockStore.On("LoadBlockMeta", height).Return(nil) blockStore.On("LoadBlockMeta", height-1).Return(nil) _, _, err := state.Rollback(blockStore, stateStore)