@@ -157,7 +157,7 @@ func NewManager(
157157 genesis * cmtypes.GenesisDoc ,
158158 store store.Store ,
159159 mempool mempool.Mempool ,
160- proxyApp proxy.AppConnConsensus ,
160+ proxyApp proxy.AppConns ,
161161 dalc * da.DAClient ,
162162 eventBus * cmtypes.EventBus ,
163163 logger log.Logger ,
@@ -217,7 +217,7 @@ func NewManager(
217217 // allow buffer for the block header and protocol encoding
218218 maxBlobSize -= blockProtocolOverhead
219219
220- exec := state .NewBlockExecutor (proposerAddress , genesis .ChainID , mempool , proxyApp , eventBus , maxBlobSize , logger , execMetrics , valSet .Hash ())
220+ exec := state .NewBlockExecutor (proposerAddress , genesis .ChainID , mempool , proxyApp . Consensus () , eventBus , maxBlobSize , logger , execMetrics , valSet .Hash ())
221221 if s .LastBlockHeight + 1 == uint64 (genesis .InitialHeight ) {
222222 res , err := exec .InitChain (genesis )
223223 if err != nil {
@@ -268,6 +268,14 @@ func NewManager(
268268 metrics : seqMetrics ,
269269 isProposer : isProposer ,
270270 }
271+
272+ if agg .conf .Replay {
273+ // Handshake to ensure that app and rollup are in sync
274+ if err := agg .Handshake (context .Background (), proxyApp ); err != nil {
275+ return nil , err
276+ }
277+ }
278+
271279 return agg , nil
272280}
273281
@@ -1166,3 +1174,124 @@ func updateState(s *types.State, res *abci.ResponseInitChain) {
11661174 s .LastResultsHash = merkle .HashFromByteSlices (nil )
11671175
11681176}
1177+
1178+ // Handshake performs the ABCI handshake with the application.
1179+ func (m * Manager ) Handshake (ctx context.Context , proxyApp proxy.AppConns ) error {
1180+ // Handshake is done via ABCI Info on the query conn.
1181+ res , err := proxyApp .Query ().Info (ctx , proxy .RequestInfo )
1182+ if err != nil {
1183+ return fmt .Errorf ("error calling Info: %v" , err )
1184+ }
1185+
1186+ blockHeight := res .LastBlockHeight
1187+ if blockHeight < 0 {
1188+ return fmt .Errorf ("got a negative last block height (%d) from the app" , blockHeight )
1189+ }
1190+ appHash := res .LastBlockAppHash
1191+
1192+ m .logger .Info ("ABCI Handshake App Info" ,
1193+ "height" , blockHeight ,
1194+ "hash" , fmt .Sprintf ("%X" , appHash ),
1195+ "software-version" , res .Version ,
1196+ "protocol-version" , res .AppVersion ,
1197+ )
1198+
1199+ // Replay blocks up to the latest in the blockstore.
1200+ appHash , err = m .ReplayBlocks (ctx , appHash , blockHeight , proxyApp )
1201+ if err != nil {
1202+ return fmt .Errorf ("error on replay: %v" , err )
1203+ }
1204+
1205+ m .logger .Info ("Completed ABCI Handshake - CometBFT and App are synced" ,
1206+ "appHeight" , blockHeight , "appHash" , fmt .Sprintf ("%X" , appHash ))
1207+
1208+ // TODO: (on restart) replay mempool
1209+
1210+ return nil
1211+ }
1212+
1213+ // ReplayBlocks replays blocks from the last state to the app's last block height.
1214+ func (m * Manager ) ReplayBlocks (
1215+ ctx context.Context ,
1216+ appHash []byte ,
1217+ appBlockHeight int64 ,
1218+ proxyApp proxy.AppConns ,
1219+ ) ([]byte , error ) {
1220+ state := m .lastState
1221+ stateBlockHeight := m .lastState .LastBlockHeight
1222+ m .logger .Info (
1223+ "ABCI Replay Blocks" ,
1224+ "appHeight" ,
1225+ appBlockHeight ,
1226+ "stateHeight" ,
1227+ stateBlockHeight )
1228+
1229+ if appBlockHeight < int64 (stateBlockHeight ) {
1230+ // the app is behind, so replay blocks
1231+ return m .replayBlocks (ctx , state , proxyApp , uint64 (appBlockHeight ), stateBlockHeight )
1232+ } else if appBlockHeight == int64 (stateBlockHeight ) {
1233+ // We're good!
1234+ assertAppHashEqualsOneFromState (appHash , state )
1235+ return appHash , nil
1236+ }
1237+
1238+ panic (fmt .Sprintf ("uncovered case! app height higher than state height, possibly need app rollback; appHeight: %d, stateHeight: %d" , appBlockHeight , stateBlockHeight ))
1239+ }
1240+
1241+ func (m * Manager ) replayBlocks (
1242+ ctx context.Context ,
1243+ state types.State ,
1244+ proxyApp proxy.AppConns ,
1245+ appBlockHeight ,
1246+ stateBlockHeight uint64 ,
1247+ ) ([]byte , error ) {
1248+ var appHash []byte
1249+ finalBlock := stateBlockHeight
1250+ firstBlock := appBlockHeight + 1
1251+ if firstBlock == 1 {
1252+ firstBlock = state .InitialHeight
1253+ }
1254+ for i := firstBlock ; i <= finalBlock ; i ++ {
1255+ select {
1256+ case <- ctx .Done ():
1257+ return nil , ctx .Err ()
1258+ default :
1259+ }
1260+
1261+ m .logger .Info ("Applying block" , "height" , i )
1262+ block , err := m .store .GetBlock (ctx , i )
1263+ if err != nil {
1264+ return nil , fmt .Errorf ("failed to get block data for height %d: %w" , i , err )
1265+ }
1266+ appHash , err = m .executor .ExecCommitBlock (proxyApp .Consensus (), block , m .logger , state , m .store )
1267+ if err != nil {
1268+ return nil , err
1269+ }
1270+ // Extra check to ensure the app was not changed in a way it shouldn't have.
1271+ if len (appHash ) > 0 {
1272+ assertAppHashEqualsOneFromBlock (appHash , block )
1273+ }
1274+ }
1275+
1276+ assertAppHashEqualsOneFromState (appHash , state )
1277+ return appHash , nil
1278+ }
1279+
1280+ func assertAppHashEqualsOneFromBlock (appHash []byte , block * types.Block ) {
1281+ if ! bytes .Equal (appHash , block .SignedHeader .AppHash ) {
1282+ panic (fmt .Sprintf (`block.AppHash does not match AppHash after replay. Got %X, expected %X.
1283+ Block: %v
1284+ ` ,
1285+ appHash , block .SignedHeader .AppHash , block ))
1286+ }
1287+ }
1288+
1289+ func assertAppHashEqualsOneFromState (appHash []byte , state types.State ) {
1290+ if ! bytes .Equal (appHash , state .AppHash ) {
1291+ panic (fmt .Sprintf (`state.AppHash does not match AppHash after replay. Got
1292+ %X, expected %X.
1293+ State: %v
1294+ Did you reset CometBFT without resetting your application's data?` ,
1295+ appHash , state .AppHash , state ))
1296+ }
1297+ }
0 commit comments