diff --git a/consensus/replay.go b/consensus/replay.go index 450c1101d..095e75d2a 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -423,6 +423,13 @@ func (h *Handshaker) ReplayBlocks( if err != nil { return nil, err } + // NOTE: There is a rare edge case where a node has upgraded from + // v0.37 with endblock to v0.38 with finalize block and thus + // does not have the app hash saved from the previous height + // here we take the appHash provided from the Info handshake + if len(finalizeBlockResponse.AgreedAppData) == 0 { + finalizeBlockResponse.AgreedAppData = appHash + } mockApp := newMockProxyApp(finalizeBlockResponse) h.logger.Info("Replay last block using mock app") state, err = h.replayBlock(state, storeBlockHeight, mockApp) diff --git a/state/store.go b/state/store.go index 107658d82..cdfce15ea 100644 --- a/state/store.go +++ b/state/store.go @@ -448,6 +448,25 @@ func (store dbStore) LoadLastFinalizeBlockResponse(height int64) (*abci.Response return nil, errors.New("expected height %d but last stored abci responses was at height %d") } + // It is possible if this is called directly after an upgrade that + // ResponseFinalizeBlock is nil. In which case we use the legacy + // ABCI responses + if info.ResponseFinalizeBlock == nil { + // sanity check + if info.LegacyAbciResponses == nil { + panic("state store contains last abci response but it is empty") + } + legacyResp := info.LegacyAbciResponses + return &abci.ResponseFinalizeBlock{ + TxResults: legacyResp.DeliverTxs, + ValidatorUpdates: legacyResp.EndBlock.ValidatorUpdates, + ConsensusParamUpdates: legacyResp.EndBlock.ConsensusParamUpdates, + Events: append(legacyResp.BeginBlock.Events, legacyResp.EndBlock.Events...), + // NOTE: AgreedAppData is missing in the response but will + // be caught and filled in consensus/replay.go + }, nil + } + return info.ResponseFinalizeBlock, nil }