github.com/Team-Kujira/tendermint@v0.34.24-indexer/consensus/replay_test.go (about) 1 package consensus 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "os" 9 "path/filepath" 10 "runtime" 11 "sort" 12 "testing" 13 "time" 14 15 "github.com/gogo/protobuf/proto" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 dbm "github.com/tendermint/tm-db" 19 20 "github.com/tendermint/tendermint/abci/example/kvstore" 21 abci "github.com/tendermint/tendermint/abci/types" 22 cfg "github.com/tendermint/tendermint/config" 23 "github.com/tendermint/tendermint/crypto" 24 cryptoenc "github.com/tendermint/tendermint/crypto/encoding" 25 "github.com/tendermint/tendermint/libs/log" 26 tmrand "github.com/tendermint/tendermint/libs/rand" 27 mempl "github.com/tendermint/tendermint/mempool" 28 "github.com/tendermint/tendermint/privval" 29 tmstate "github.com/tendermint/tendermint/proto/tendermint/state" 30 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 31 "github.com/tendermint/tendermint/proxy" 32 sm "github.com/tendermint/tendermint/state" 33 "github.com/tendermint/tendermint/types" 34 ) 35 36 func TestMain(m *testing.M) { 37 config = ResetConfig("consensus_reactor_test") 38 consensusReplayConfig = ResetConfig("consensus_replay_test") 39 configStateTest := ResetConfig("consensus_state_test") 40 configMempoolTest := ResetConfig("consensus_mempool_test") 41 configByzantineTest := ResetConfig("consensus_byzantine_test") 42 code := m.Run() 43 os.RemoveAll(config.RootDir) 44 os.RemoveAll(consensusReplayConfig.RootDir) 45 os.RemoveAll(configStateTest.RootDir) 46 os.RemoveAll(configMempoolTest.RootDir) 47 os.RemoveAll(configByzantineTest.RootDir) 48 os.Exit(code) 49 } 50 51 // These tests ensure we can always recover from failure at any part of the consensus process. 52 // There are two general failure scenarios: failure during consensus, and failure while applying the block. 53 // Only the latter interacts with the app and store, 54 // but the former has to deal with restrictions on re-use of priv_validator keys. 55 // The `WAL Tests` are for failures during the consensus; 56 // the `Handshake Tests` are for failures in applying the block. 57 // With the help of the WAL, we can recover from it all! 58 59 //------------------------------------------------------------------------------------------ 60 // WAL Tests 61 62 // TODO: It would be better to verify explicitly which states we can recover from without the wal 63 // and which ones we need the wal for - then we'd also be able to only flush the 64 // wal writer when we need to, instead of with every message. 65 66 func startNewStateAndWaitForBlock(t *testing.T, consensusReplayConfig *cfg.Config, 67 lastBlockHeight int64, blockDB dbm.DB, stateStore sm.Store, 68 ) { 69 logger := log.TestingLogger() 70 state, _ := stateStore.LoadFromDBOrGenesisFile(consensusReplayConfig.GenesisFile()) 71 privValidator := loadPrivValidator(consensusReplayConfig) 72 cs := newStateWithConfigAndBlockStore( 73 consensusReplayConfig, 74 state, 75 privValidator, 76 kvstore.NewApplication(), 77 blockDB, 78 ) 79 cs.SetLogger(logger) 80 81 bytes, _ := os.ReadFile(cs.config.WalFile()) 82 t.Logf("====== WAL: \n\r%X\n", bytes) 83 84 err := cs.Start() 85 require.NoError(t, err) 86 defer func() { 87 if err := cs.Stop(); err != nil { 88 t.Error(err) 89 } 90 }() 91 92 // This is just a signal that we haven't halted; its not something contained 93 // in the WAL itself. Assuming the consensus state is running, replay of any 94 // WAL, including the empty one, should eventually be followed by a new 95 // block, or else something is wrong. 96 newBlockSub, err := cs.eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock) 97 require.NoError(t, err) 98 select { 99 case <-newBlockSub.Out(): 100 case <-newBlockSub.Cancelled(): 101 t.Fatal("newBlockSub was cancelled") 102 case <-time.After(120 * time.Second): 103 t.Fatal("Timed out waiting for new block (see trace above)") 104 } 105 } 106 107 func sendTxs(ctx context.Context, cs *State) { 108 for i := 0; i < 256; i++ { 109 select { 110 case <-ctx.Done(): 111 return 112 default: 113 tx := []byte{byte(i)} 114 if err := assertMempool(cs.txNotifier).CheckTx(tx, nil, mempl.TxInfo{}); err != nil { 115 panic(err) 116 } 117 i++ 118 } 119 } 120 } 121 122 // TestWALCrash uses crashing WAL to test we can recover from any WAL failure. 123 func TestWALCrash(t *testing.T) { 124 testCases := []struct { 125 name string 126 initFn func(dbm.DB, *State, context.Context) 127 heightToStop int64 128 }{ 129 { 130 "empty block", 131 func(stateDB dbm.DB, cs *State, ctx context.Context) {}, 132 1, 133 }, 134 { 135 "many non-empty blocks", 136 func(stateDB dbm.DB, cs *State, ctx context.Context) { 137 go sendTxs(ctx, cs) 138 }, 139 3, 140 }, 141 } 142 143 for i, tc := range testCases { 144 tc := tc 145 consensusReplayConfig := ResetConfig(fmt.Sprintf("%s_%d", t.Name(), i)) 146 t.Run(tc.name, func(t *testing.T) { 147 crashWALandCheckLiveness(t, consensusReplayConfig, tc.initFn, tc.heightToStop) 148 }) 149 } 150 } 151 152 func crashWALandCheckLiveness(t *testing.T, consensusReplayConfig *cfg.Config, 153 initFn func(dbm.DB, *State, context.Context), heightToStop int64, 154 ) { 155 walPanicked := make(chan error) 156 crashingWal := &crashingWAL{panicCh: walPanicked, heightToStop: heightToStop} 157 158 i := 1 159 LOOP: 160 for { 161 t.Logf("====== LOOP %d\n", i) 162 163 // create consensus state from a clean slate 164 logger := log.NewNopLogger() 165 blockDB := dbm.NewMemDB() 166 stateDB := blockDB 167 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 168 DiscardABCIResponses: false, 169 }) 170 state, err := sm.MakeGenesisStateFromFile(consensusReplayConfig.GenesisFile()) 171 require.NoError(t, err) 172 privValidator := loadPrivValidator(consensusReplayConfig) 173 cs := newStateWithConfigAndBlockStore( 174 consensusReplayConfig, 175 state, 176 privValidator, 177 kvstore.NewApplication(), 178 blockDB, 179 ) 180 cs.SetLogger(logger) 181 182 // start sending transactions 183 ctx, cancel := context.WithCancel(context.Background()) 184 initFn(stateDB, cs, ctx) 185 186 // clean up WAL file from the previous iteration 187 walFile := cs.config.WalFile() 188 os.Remove(walFile) 189 190 // set crashing WAL 191 csWal, err := cs.OpenWAL(walFile) 192 require.NoError(t, err) 193 crashingWal.next = csWal 194 195 // reset the message counter 196 crashingWal.msgIndex = 1 197 cs.wal = crashingWal 198 199 // start consensus state 200 err = cs.Start() 201 require.NoError(t, err) 202 203 i++ 204 205 select { 206 case err := <-walPanicked: 207 t.Logf("WAL panicked: %v", err) 208 209 // make sure we can make blocks after a crash 210 startNewStateAndWaitForBlock(t, consensusReplayConfig, cs.Height, blockDB, stateStore) 211 212 // stop consensus state and transactions sender (initFn) 213 cs.Stop() //nolint:errcheck // Logging this error causes failure 214 cancel() 215 216 // if we reached the required height, exit 217 if _, ok := err.(ReachedHeightToStopError); ok { 218 break LOOP 219 } 220 case <-time.After(10 * time.Second): 221 t.Fatal("WAL did not panic for 10 seconds (check the log)") 222 } 223 } 224 } 225 226 // crashingWAL is a WAL which crashes or rather simulates a crash during Save 227 // (before and after). It remembers a message for which we last panicked 228 // (lastPanickedForMsgIndex), so we don't panic for it in subsequent iterations. 229 type crashingWAL struct { 230 next WAL 231 panicCh chan error 232 heightToStop int64 233 234 msgIndex int // current message index 235 lastPanickedForMsgIndex int // last message for which we panicked 236 } 237 238 var _ WAL = &crashingWAL{} 239 240 // WALWriteError indicates a WAL crash. 241 type WALWriteError struct { 242 msg string 243 } 244 245 func (e WALWriteError) Error() string { 246 return e.msg 247 } 248 249 // ReachedHeightToStopError indicates we've reached the required consensus 250 // height and may exit. 251 type ReachedHeightToStopError struct { 252 height int64 253 } 254 255 func (e ReachedHeightToStopError) Error() string { 256 return fmt.Sprintf("reached height to stop %d", e.height) 257 } 258 259 // Write simulate WAL's crashing by sending an error to the panicCh and then 260 // exiting the cs.receiveRoutine. 261 func (w *crashingWAL) Write(m WALMessage) error { 262 if endMsg, ok := m.(EndHeightMessage); ok { 263 if endMsg.Height == w.heightToStop { 264 w.panicCh <- ReachedHeightToStopError{endMsg.Height} 265 runtime.Goexit() 266 return nil 267 } 268 269 return w.next.Write(m) 270 } 271 272 if w.msgIndex > w.lastPanickedForMsgIndex { 273 w.lastPanickedForMsgIndex = w.msgIndex 274 _, file, line, _ := runtime.Caller(1) 275 w.panicCh <- WALWriteError{fmt.Sprintf("failed to write %T to WAL (fileline: %s:%d)", m, file, line)} 276 runtime.Goexit() 277 return nil 278 } 279 280 w.msgIndex++ 281 return w.next.Write(m) 282 } 283 284 func (w *crashingWAL) WriteSync(m WALMessage) error { 285 return w.Write(m) 286 } 287 288 func (w *crashingWAL) FlushAndSync() error { return w.next.FlushAndSync() } 289 290 func (w *crashingWAL) SearchForEndHeight( 291 height int64, 292 options *WALSearchOptions, 293 ) (rd io.ReadCloser, found bool, err error) { 294 return w.next.SearchForEndHeight(height, options) 295 } 296 297 func (w *crashingWAL) Start() error { return w.next.Start() } 298 func (w *crashingWAL) Stop() error { return w.next.Stop() } 299 func (w *crashingWAL) Wait() { w.next.Wait() } 300 301 // ------------------------------------------------------------------------------------------ 302 type testSim struct { 303 GenesisState sm.State 304 Config *cfg.Config 305 Chain []*types.Block 306 Commits []*types.Commit 307 CleanupFunc cleanupFunc 308 } 309 310 const ( 311 numBlocks = 6 312 ) 313 314 var ( 315 mempool = emptyMempool{} 316 evpool = sm.EmptyEvidencePool{} 317 318 sim testSim 319 ) 320 321 //--------------------------------------- 322 // Test handshake/replay 323 324 // 0 - all synced up 325 // 1 - saved block but app and state are behind 326 // 2 - save block and committed but state is behind 327 // 3 - save block and committed with truncated block store and state behind 328 var modes = []uint{0, 1, 2, 3} 329 330 // This is actually not a test, it's for storing validator change tx data for testHandshakeReplay 331 func TestSimulateValidatorsChange(t *testing.T) { 332 nPeers := 7 333 nVals := 4 334 css, genDoc, config, cleanup := randConsensusNetWithPeers( 335 nVals, 336 nPeers, 337 "replay_test", 338 newMockTickerFunc(true), 339 newPersistentKVStoreWithPath) 340 sim.Config = config 341 sim.GenesisState, _ = sm.MakeGenesisState(genDoc) 342 sim.CleanupFunc = cleanup 343 344 partSize := types.BlockPartSizeBytes 345 346 newRoundCh := subscribe(css[0].eventBus, types.EventQueryNewRound) 347 proposalCh := subscribe(css[0].eventBus, types.EventQueryCompleteProposal) 348 349 vss := make([]*validatorStub, nPeers) 350 for i := 0; i < nPeers; i++ { 351 vss[i] = newValidatorStub(css[i].privValidator, int32(i)) 352 } 353 height, round := css[0].Height, css[0].Round 354 355 // start the machine 356 startTestRound(css[0], height, round) 357 incrementHeight(vss...) 358 ensureNewRound(newRoundCh, height, 0) 359 ensureNewProposal(proposalCh, height, round) 360 rs := css[0].GetRoundState() 361 signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:nVals]...) 362 ensureNewRound(newRoundCh, height+1, 0) 363 364 // HEIGHT 2 365 height++ 366 incrementHeight(vss...) 367 newValidatorPubKey1, err := css[nVals].privValidator.GetPubKey() 368 require.NoError(t, err) 369 valPubKey1ABCI, err := cryptoenc.PubKeyToProto(newValidatorPubKey1) 370 require.NoError(t, err) 371 newValidatorTx1 := kvstore.MakeValSetChangeTx(valPubKey1ABCI, testMinPower) 372 err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx1, nil, mempl.TxInfo{}) 373 assert.Nil(t, err) 374 propBlock, _ := css[0].createProposalBlock() // changeProposer(t, cs1, vs2) 375 propBlockParts := propBlock.MakePartSet(partSize) 376 blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} 377 378 proposal := types.NewProposal(vss[1].Height, round, -1, blockID) 379 p := proposal.ToProto() 380 if err := vss[1].SignProposal(config.ChainID(), p); err != nil { 381 t.Fatal("failed to sign bad proposal", err) 382 } 383 proposal.Signature = p.Signature 384 385 // set the proposal block 386 if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil { 387 t.Fatal(err) 388 } 389 ensureNewProposal(proposalCh, height, round) 390 rs = css[0].GetRoundState() 391 signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:nVals]...) 392 ensureNewRound(newRoundCh, height+1, 0) 393 394 // HEIGHT 3 395 height++ 396 incrementHeight(vss...) 397 updateValidatorPubKey1, err := css[nVals].privValidator.GetPubKey() 398 require.NoError(t, err) 399 updatePubKey1ABCI, err := cryptoenc.PubKeyToProto(updateValidatorPubKey1) 400 require.NoError(t, err) 401 updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25) 402 err = assertMempool(css[0].txNotifier).CheckTx(updateValidatorTx1, nil, mempl.TxInfo{}) 403 assert.Nil(t, err) 404 propBlock, _ = css[0].createProposalBlock() // changeProposer(t, cs1, vs2) 405 propBlockParts = propBlock.MakePartSet(partSize) 406 blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} 407 408 proposal = types.NewProposal(vss[2].Height, round, -1, blockID) 409 p = proposal.ToProto() 410 if err := vss[2].SignProposal(config.ChainID(), p); err != nil { 411 t.Fatal("failed to sign bad proposal", err) 412 } 413 proposal.Signature = p.Signature 414 415 // set the proposal block 416 if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil { 417 t.Fatal(err) 418 } 419 ensureNewProposal(proposalCh, height, round) 420 rs = css[0].GetRoundState() 421 signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:nVals]...) 422 ensureNewRound(newRoundCh, height+1, 0) 423 424 // HEIGHT 4 425 height++ 426 incrementHeight(vss...) 427 newValidatorPubKey2, err := css[nVals+1].privValidator.GetPubKey() 428 require.NoError(t, err) 429 newVal2ABCI, err := cryptoenc.PubKeyToProto(newValidatorPubKey2) 430 require.NoError(t, err) 431 newValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, testMinPower) 432 err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx2, nil, mempl.TxInfo{}) 433 assert.Nil(t, err) 434 newValidatorPubKey3, err := css[nVals+2].privValidator.GetPubKey() 435 require.NoError(t, err) 436 newVal3ABCI, err := cryptoenc.PubKeyToProto(newValidatorPubKey3) 437 require.NoError(t, err) 438 newValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, testMinPower) 439 err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx3, nil, mempl.TxInfo{}) 440 assert.Nil(t, err) 441 propBlock, _ = css[0].createProposalBlock() // changeProposer(t, cs1, vs2) 442 propBlockParts = propBlock.MakePartSet(partSize) 443 blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} 444 newVss := make([]*validatorStub, nVals+1) 445 copy(newVss, vss[:nVals+1]) 446 sort.Sort(ValidatorStubsByPower(newVss)) 447 448 valIndexFn := func(cssIdx int) int { 449 for i, vs := range newVss { 450 vsPubKey, err := vs.GetPubKey() 451 require.NoError(t, err) 452 453 cssPubKey, err := css[cssIdx].privValidator.GetPubKey() 454 require.NoError(t, err) 455 456 if vsPubKey.Equals(cssPubKey) { 457 return i 458 } 459 } 460 panic(fmt.Sprintf("validator css[%d] not found in newVss", cssIdx)) 461 } 462 463 selfIndex := valIndexFn(0) 464 465 proposal = types.NewProposal(vss[3].Height, round, -1, blockID) 466 p = proposal.ToProto() 467 if err := vss[3].SignProposal(config.ChainID(), p); err != nil { 468 t.Fatal("failed to sign bad proposal", err) 469 } 470 proposal.Signature = p.Signature 471 472 // set the proposal block 473 if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil { 474 t.Fatal(err) 475 } 476 ensureNewProposal(proposalCh, height, round) 477 478 removeValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, 0) 479 err = assertMempool(css[0].txNotifier).CheckTx(removeValidatorTx2, nil, mempl.TxInfo{}) 480 assert.Nil(t, err) 481 482 rs = css[0].GetRoundState() 483 for i := 0; i < nVals+1; i++ { 484 if i == selfIndex { 485 continue 486 } 487 signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), newVss[i]) 488 } 489 490 ensureNewRound(newRoundCh, height+1, 0) 491 492 // HEIGHT 5 493 height++ 494 incrementHeight(vss...) 495 // Reflect the changes to vss[nVals] at height 3 and resort newVss. 496 newVssIdx := valIndexFn(nVals) 497 newVss[newVssIdx].VotingPower = 25 498 sort.Sort(ValidatorStubsByPower(newVss)) 499 selfIndex = valIndexFn(0) 500 ensureNewProposal(proposalCh, height, round) 501 rs = css[0].GetRoundState() 502 for i := 0; i < nVals+1; i++ { 503 if i == selfIndex { 504 continue 505 } 506 signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), newVss[i]) 507 } 508 ensureNewRound(newRoundCh, height+1, 0) 509 510 // HEIGHT 6 511 height++ 512 incrementHeight(vss...) 513 removeValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, 0) 514 err = assertMempool(css[0].txNotifier).CheckTx(removeValidatorTx3, nil, mempl.TxInfo{}) 515 assert.Nil(t, err) 516 propBlock, _ = css[0].createProposalBlock() // changeProposer(t, cs1, vs2) 517 propBlockParts = propBlock.MakePartSet(partSize) 518 blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} 519 newVss = make([]*validatorStub, nVals+3) 520 copy(newVss, vss[:nVals+3]) 521 sort.Sort(ValidatorStubsByPower(newVss)) 522 523 selfIndex = valIndexFn(0) 524 proposal = types.NewProposal(vss[1].Height, round, -1, blockID) 525 p = proposal.ToProto() 526 if err := vss[1].SignProposal(config.ChainID(), p); err != nil { 527 t.Fatal("failed to sign bad proposal", err) 528 } 529 proposal.Signature = p.Signature 530 531 // set the proposal block 532 if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil { 533 t.Fatal(err) 534 } 535 ensureNewProposal(proposalCh, height, round) 536 rs = css[0].GetRoundState() 537 for i := 0; i < nVals+3; i++ { 538 if i == selfIndex { 539 continue 540 } 541 signAddVotes(css[0], tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), newVss[i]) 542 } 543 ensureNewRound(newRoundCh, height+1, 0) 544 545 sim.Chain = make([]*types.Block, 0) 546 sim.Commits = make([]*types.Commit, 0) 547 for i := 1; i <= numBlocks; i++ { 548 sim.Chain = append(sim.Chain, css[0].blockStore.LoadBlock(int64(i))) 549 sim.Commits = append(sim.Commits, css[0].blockStore.LoadBlockCommit(int64(i))) 550 } 551 } 552 553 // Sync from scratch 554 func TestHandshakeReplayAll(t *testing.T) { 555 for _, m := range modes { 556 testHandshakeReplay(t, config, 0, m, false) 557 } 558 for _, m := range modes { 559 testHandshakeReplay(t, config, 0, m, true) 560 } 561 } 562 563 // Sync many, not from scratch 564 func TestHandshakeReplaySome(t *testing.T) { 565 for _, m := range modes { 566 testHandshakeReplay(t, config, 2, m, false) 567 } 568 for _, m := range modes { 569 testHandshakeReplay(t, config, 2, m, true) 570 } 571 } 572 573 // Sync from lagging by one 574 func TestHandshakeReplayOne(t *testing.T) { 575 for _, m := range modes { 576 testHandshakeReplay(t, config, numBlocks-1, m, false) 577 } 578 for _, m := range modes { 579 testHandshakeReplay(t, config, numBlocks-1, m, true) 580 } 581 } 582 583 // Sync from caught up 584 func TestHandshakeReplayNone(t *testing.T) { 585 for _, m := range modes { 586 testHandshakeReplay(t, config, numBlocks, m, false) 587 } 588 for _, m := range modes { 589 testHandshakeReplay(t, config, numBlocks, m, true) 590 } 591 } 592 593 // Test mockProxyApp should not panic when app return ABCIResponses with some empty ResponseDeliverTx 594 func TestMockProxyApp(t *testing.T) { 595 sim.CleanupFunc() // clean the test env created in TestSimulateValidatorsChange 596 logger := log.TestingLogger() 597 validTxs, invalidTxs := 0, 0 598 txIndex := 0 599 600 assert.NotPanics(t, func() { 601 abciResWithEmptyDeliverTx := new(tmstate.ABCIResponses) 602 abciResWithEmptyDeliverTx.DeliverTxs = make([]*abci.ResponseDeliverTx, 0) 603 abciResWithEmptyDeliverTx.DeliverTxs = append(abciResWithEmptyDeliverTx.DeliverTxs, &abci.ResponseDeliverTx{}) 604 605 // called when saveABCIResponses: 606 bytes, err := proto.Marshal(abciResWithEmptyDeliverTx) 607 require.NoError(t, err) 608 loadedAbciRes := new(tmstate.ABCIResponses) 609 610 // this also happens sm.LoadABCIResponses 611 err = proto.Unmarshal(bytes, loadedAbciRes) 612 require.NoError(t, err) 613 614 mock := newMockProxyApp([]byte("mock_hash"), loadedAbciRes) 615 616 abciRes := new(tmstate.ABCIResponses) 617 abciRes.DeliverTxs = make([]*abci.ResponseDeliverTx, len(loadedAbciRes.DeliverTxs)) 618 // Execute transactions and get hash. 619 proxyCb := func(req *abci.Request, res *abci.Response) { 620 if r, ok := res.Value.(*abci.Response_DeliverTx); ok { 621 // TODO: make use of res.Log 622 // TODO: make use of this info 623 // Blocks may include invalid txs. 624 txRes := r.DeliverTx 625 if txRes.Code == abci.CodeTypeOK { 626 validTxs++ 627 } else { 628 logger.Debug("Invalid tx", "code", txRes.Code, "log", txRes.Log) 629 invalidTxs++ 630 } 631 abciRes.DeliverTxs[txIndex] = txRes 632 txIndex++ 633 } 634 } 635 mock.SetResponseCallback(proxyCb) 636 637 someTx := []byte("tx") 638 mock.DeliverTxAsync(abci.RequestDeliverTx{Tx: someTx}) 639 }) 640 assert.True(t, validTxs == 1) 641 assert.True(t, invalidTxs == 0) 642 } 643 644 func tempWALWithData(data []byte) string { 645 walFile, err := os.CreateTemp("", "wal") 646 if err != nil { 647 panic(fmt.Sprintf("failed to create temp WAL file: %v", err)) 648 } 649 _, err = walFile.Write(data) 650 if err != nil { 651 panic(fmt.Sprintf("failed to write to temp WAL file: %v", err)) 652 } 653 if err := walFile.Close(); err != nil { 654 panic(fmt.Sprintf("failed to close temp WAL file: %v", err)) 655 } 656 return walFile.Name() 657 } 658 659 // Make some blocks. Start a fresh app and apply nBlocks blocks. 660 // Then restart the app and sync it up with the remaining blocks 661 func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uint, testValidatorsChange bool) { 662 var chain []*types.Block 663 var commits []*types.Commit 664 var store *mockBlockStore 665 var stateDB dbm.DB 666 var genesisState sm.State 667 if testValidatorsChange { 668 testConfig := ResetConfig(fmt.Sprintf("%s_%v_m", t.Name(), mode)) 669 defer os.RemoveAll(testConfig.RootDir) 670 stateDB = dbm.NewMemDB() 671 672 genesisState = sim.GenesisState 673 config = sim.Config 674 chain = append([]*types.Block{}, sim.Chain...) // copy chain 675 commits = sim.Commits 676 store = newMockBlockStore(config, genesisState.ConsensusParams) 677 } else { // test single node 678 testConfig := ResetConfig(fmt.Sprintf("%s_%v_s", t.Name(), mode)) 679 defer os.RemoveAll(testConfig.RootDir) 680 walBody, err := WALWithNBlocks(t, numBlocks) 681 require.NoError(t, err) 682 walFile := tempWALWithData(walBody) 683 config.Consensus.SetWalFile(walFile) 684 685 privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) 686 687 wal, err := NewWAL(walFile) 688 require.NoError(t, err) 689 wal.SetLogger(log.TestingLogger()) 690 err = wal.Start() 691 require.NoError(t, err) 692 t.Cleanup(func() { 693 if err := wal.Stop(); err != nil { 694 t.Error(err) 695 } 696 }) 697 chain, commits, err = makeBlockchainFromWAL(wal) 698 require.NoError(t, err) 699 pubKey, err := privVal.GetPubKey() 700 require.NoError(t, err) 701 stateDB, genesisState, store = stateAndStore(config, pubKey, kvstore.ProtocolVersion) 702 703 } 704 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 705 DiscardABCIResponses: false, 706 }) 707 store.chain = chain 708 store.commits = commits 709 710 state := genesisState.Copy() 711 // run the chain through state.ApplyBlock to build up the tendermint state 712 state = buildTMStateFromChain(config, stateStore, state, chain, nBlocks, mode) 713 latestAppHash := state.AppHash 714 715 // make a new client creator 716 kvstoreApp := kvstore.NewPersistentKVStoreApplication( 717 filepath.Join(config.DBDir(), fmt.Sprintf("replay_test_%d_%d_a", nBlocks, mode))) 718 719 clientCreator2 := proxy.NewLocalClientCreator(kvstoreApp) 720 if nBlocks > 0 { 721 // run nBlocks against a new client to build up the app state. 722 // use a throwaway tendermint state 723 proxyApp := proxy.NewAppConns(clientCreator2) 724 stateDB1 := dbm.NewMemDB() 725 stateStore := sm.NewStore(stateDB1, sm.StoreOptions{ 726 DiscardABCIResponses: false, 727 }) 728 err := stateStore.Save(genesisState) 729 require.NoError(t, err) 730 buildAppStateFromChain(proxyApp, stateStore, genesisState, chain, nBlocks, mode) 731 } 732 733 // Prune block store if requested 734 expectError := false 735 if mode == 3 { 736 pruned, err := store.PruneBlocks(2) 737 require.NoError(t, err) 738 require.EqualValues(t, 1, pruned) 739 expectError = int64(nBlocks) < 2 740 } 741 742 // now start the app using the handshake - it should sync 743 genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) 744 handshaker := NewHandshaker(stateStore, state, store, genDoc) 745 proxyApp := proxy.NewAppConns(clientCreator2) 746 if err := proxyApp.Start(); err != nil { 747 t.Fatalf("Error starting proxy app connections: %v", err) 748 } 749 750 t.Cleanup(func() { 751 if err := proxyApp.Stop(); err != nil { 752 t.Error(err) 753 } 754 }) 755 756 err := handshaker.Handshake(proxyApp) 757 if expectError { 758 require.Error(t, err) 759 return 760 } else if err != nil { 761 t.Fatalf("Error on abci handshake: %v", err) 762 } 763 764 // get the latest app hash from the app 765 res, err := proxyApp.Query().InfoSync(abci.RequestInfo{Version: ""}) 766 if err != nil { 767 t.Fatal(err) 768 } 769 770 // the app hash should be synced up 771 if !bytes.Equal(latestAppHash, res.LastBlockAppHash) { 772 t.Fatalf( 773 "Expected app hashes to match after handshake/replay. got %X, expected %X", 774 res.LastBlockAppHash, 775 latestAppHash) 776 } 777 778 expectedBlocksToSync := numBlocks - nBlocks 779 if nBlocks == numBlocks && mode > 0 { 780 expectedBlocksToSync++ 781 } else if nBlocks > 0 && mode == 1 { 782 expectedBlocksToSync++ 783 } 784 785 if handshaker.NBlocks() != expectedBlocksToSync { 786 t.Fatalf("Expected handshake to sync %d blocks, got %d", expectedBlocksToSync, handshaker.NBlocks()) 787 } 788 } 789 790 func applyBlock(stateStore sm.Store, st sm.State, blk *types.Block, proxyApp proxy.AppConns) sm.State { 791 testPartSize := types.BlockPartSizeBytes 792 blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool) 793 794 blkID := types.BlockID{Hash: blk.Hash(), PartSetHeader: blk.MakePartSet(testPartSize).Header()} 795 newState, _, err := blockExec.ApplyBlock(st, blkID, blk) 796 if err != nil { 797 panic(err) 798 } 799 return newState 800 } 801 802 func buildAppStateFromChain(proxyApp proxy.AppConns, stateStore sm.Store, 803 state sm.State, chain []*types.Block, nBlocks int, mode uint, 804 ) { 805 // start a new app without handshake, play nBlocks blocks 806 if err := proxyApp.Start(); err != nil { 807 panic(err) 808 } 809 defer proxyApp.Stop() //nolint:errcheck // ignore 810 811 state.Version.Consensus.App = kvstore.ProtocolVersion // simulate handshake, receive app version 812 validators := types.TM2PB.ValidatorUpdates(state.Validators) 813 if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{ 814 Validators: validators, 815 }); err != nil { 816 panic(err) 817 } 818 if err := stateStore.Save(state); err != nil { // save height 1's validatorsInfo 819 panic(err) 820 } 821 switch mode { 822 case 0: 823 for i := 0; i < nBlocks; i++ { 824 block := chain[i] 825 state = applyBlock(stateStore, state, block, proxyApp) 826 } 827 case 1, 2, 3: 828 for i := 0; i < nBlocks-1; i++ { 829 block := chain[i] 830 state = applyBlock(stateStore, state, block, proxyApp) 831 } 832 833 if mode == 2 || mode == 3 { 834 // update the kvstore height and apphash 835 // as if we ran commit but not 836 state = applyBlock(stateStore, state, chain[nBlocks-1], proxyApp) 837 } 838 default: 839 panic(fmt.Sprintf("unknown mode %v", mode)) 840 } 841 } 842 843 func buildTMStateFromChain( 844 config *cfg.Config, 845 stateStore sm.Store, 846 state sm.State, 847 chain []*types.Block, 848 nBlocks int, 849 mode uint, 850 ) sm.State { 851 // run the whole chain against this client to build up the tendermint state 852 clientCreator := proxy.NewLocalClientCreator( 853 kvstore.NewPersistentKVStoreApplication( 854 filepath.Join(config.DBDir(), fmt.Sprintf("replay_test_%d_%d_t", nBlocks, mode)))) 855 proxyApp := proxy.NewAppConns(clientCreator) 856 if err := proxyApp.Start(); err != nil { 857 panic(err) 858 } 859 defer proxyApp.Stop() //nolint:errcheck 860 861 state.Version.Consensus.App = kvstore.ProtocolVersion // simulate handshake, receive app version 862 validators := types.TM2PB.ValidatorUpdates(state.Validators) 863 if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{ 864 Validators: validators, 865 }); err != nil { 866 panic(err) 867 } 868 if err := stateStore.Save(state); err != nil { // save height 1's validatorsInfo 869 panic(err) 870 } 871 switch mode { 872 case 0: 873 // sync right up 874 for _, block := range chain { 875 state = applyBlock(stateStore, state, block, proxyApp) 876 } 877 878 case 1, 2, 3: 879 // sync up to the penultimate as if we stored the block. 880 // whether we commit or not depends on the appHash 881 for _, block := range chain[:len(chain)-1] { 882 state = applyBlock(stateStore, state, block, proxyApp) 883 } 884 885 // apply the final block to a state copy so we can 886 // get the right next appHash but keep the state back 887 applyBlock(stateStore, state, chain[len(chain)-1], proxyApp) 888 default: 889 panic(fmt.Sprintf("unknown mode %v", mode)) 890 } 891 892 return state 893 } 894 895 func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) { 896 // 1. Initialize tendermint and commit 3 blocks with the following app hashes: 897 // - 0x01 898 // - 0x02 899 // - 0x03 900 config := ResetConfig("handshake_test_") 901 defer os.RemoveAll(config.RootDir) 902 privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) 903 const appVersion = 0x0 904 pubKey, err := privVal.GetPubKey() 905 require.NoError(t, err) 906 stateDB, state, store := stateAndStore(config, pubKey, appVersion) 907 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 908 DiscardABCIResponses: false, 909 }) 910 genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) 911 state.LastValidators = state.Validators.Copy() 912 // mode = 0 for committing all the blocks 913 blocks := makeBlocks(3, &state, privVal) 914 store.chain = blocks 915 916 // 2. Tendermint must panic if app returns wrong hash for the first block 917 // - RANDOM HASH 918 // - 0x02 919 // - 0x03 920 { 921 app := &badApp{numBlocks: 3, allHashesAreWrong: true} 922 clientCreator := proxy.NewLocalClientCreator(app) 923 proxyApp := proxy.NewAppConns(clientCreator) 924 err := proxyApp.Start() 925 require.NoError(t, err) 926 t.Cleanup(func() { 927 if err := proxyApp.Stop(); err != nil { 928 t.Error(err) 929 } 930 }) 931 932 assert.Panics(t, func() { 933 h := NewHandshaker(stateStore, state, store, genDoc) 934 if err = h.Handshake(proxyApp); err != nil { 935 t.Log(err) 936 } 937 }) 938 } 939 940 // 3. Tendermint must panic if app returns wrong hash for the last block 941 // - 0x01 942 // - 0x02 943 // - RANDOM HASH 944 { 945 app := &badApp{numBlocks: 3, onlyLastHashIsWrong: true} 946 clientCreator := proxy.NewLocalClientCreator(app) 947 proxyApp := proxy.NewAppConns(clientCreator) 948 err := proxyApp.Start() 949 require.NoError(t, err) 950 t.Cleanup(func() { 951 if err := proxyApp.Stop(); err != nil { 952 t.Error(err) 953 } 954 }) 955 956 assert.Panics(t, func() { 957 h := NewHandshaker(stateStore, state, store, genDoc) 958 if err = h.Handshake(proxyApp); err != nil { 959 t.Log(err) 960 } 961 }) 962 } 963 } 964 965 func makeBlocks(n int, state *sm.State, privVal types.PrivValidator) []*types.Block { 966 blocks := make([]*types.Block, 0) 967 968 var ( 969 prevBlock *types.Block 970 prevBlockMeta *types.BlockMeta 971 ) 972 973 appHeight := byte(0x01) 974 for i := 0; i < n; i++ { 975 height := int64(i + 1) 976 977 block, parts := makeBlock(*state, prevBlock, prevBlockMeta, privVal, height) 978 blocks = append(blocks, block) 979 980 prevBlock = block 981 prevBlockMeta = types.NewBlockMeta(block, parts) 982 983 // update state 984 state.AppHash = []byte{appHeight} 985 appHeight++ 986 state.LastBlockHeight = height 987 } 988 989 return blocks 990 } 991 992 func makeBlock(state sm.State, lastBlock *types.Block, lastBlockMeta *types.BlockMeta, 993 privVal types.PrivValidator, height int64, 994 ) (*types.Block, *types.PartSet) { 995 lastCommit := types.NewCommit(height-1, 0, types.BlockID{}, nil) 996 if height > 1 { 997 vote, _ := types.MakeVote( 998 lastBlock.Header.Height, 999 lastBlockMeta.BlockID, 1000 state.Validators, 1001 privVal, 1002 lastBlock.Header.ChainID, 1003 time.Now()) 1004 lastCommit = types.NewCommit(vote.Height, vote.Round, 1005 lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) 1006 } 1007 1008 return state.MakeBlock(height, []types.Tx{}, lastCommit, nil, state.Validators.GetProposer().Address) 1009 } 1010 1011 type badApp struct { 1012 abci.BaseApplication 1013 numBlocks byte 1014 height byte 1015 allHashesAreWrong bool 1016 onlyLastHashIsWrong bool 1017 } 1018 1019 func (app *badApp) Commit() abci.ResponseCommit { 1020 app.height++ 1021 if app.onlyLastHashIsWrong { 1022 if app.height == app.numBlocks { 1023 return abci.ResponseCommit{Data: tmrand.Bytes(8)} 1024 } 1025 return abci.ResponseCommit{Data: []byte{app.height}} 1026 } else if app.allHashesAreWrong { 1027 return abci.ResponseCommit{Data: tmrand.Bytes(8)} 1028 } 1029 1030 panic("either allHashesAreWrong or onlyLastHashIsWrong must be set") 1031 } 1032 1033 //-------------------------- 1034 // utils for making blocks 1035 1036 func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) { 1037 var height int64 1038 1039 // Search for height marker 1040 gr, found, err := wal.SearchForEndHeight(height, &WALSearchOptions{}) 1041 if err != nil { 1042 return nil, nil, err 1043 } 1044 if !found { 1045 return nil, nil, fmt.Errorf("wal does not contain height %d", height) 1046 } 1047 defer gr.Close() 1048 1049 // log.Notice("Build a blockchain by reading from the WAL") 1050 1051 var ( 1052 blocks []*types.Block 1053 commits []*types.Commit 1054 thisBlockParts *types.PartSet 1055 thisBlockCommit *types.Commit 1056 ) 1057 1058 dec := NewWALDecoder(gr) 1059 for { 1060 msg, err := dec.Decode() 1061 if err == io.EOF { 1062 break 1063 } else if err != nil { 1064 return nil, nil, err 1065 } 1066 1067 piece := readPieceFromWAL(msg) 1068 if piece == nil { 1069 continue 1070 } 1071 1072 switch p := piece.(type) { 1073 case EndHeightMessage: 1074 // if its not the first one, we have a full block 1075 if thisBlockParts != nil { 1076 pbb := new(tmproto.Block) 1077 bz, err := io.ReadAll(thisBlockParts.GetReader()) 1078 if err != nil { 1079 panic(err) 1080 } 1081 err = proto.Unmarshal(bz, pbb) 1082 if err != nil { 1083 panic(err) 1084 } 1085 block, err := types.BlockFromProto(pbb) 1086 if err != nil { 1087 panic(err) 1088 } 1089 1090 if block.Height != height+1 { 1091 panic(fmt.Sprintf("read bad block from wal. got height %d, expected %d", block.Height, height+1)) 1092 } 1093 commitHeight := thisBlockCommit.Height 1094 if commitHeight != height+1 { 1095 panic(fmt.Sprintf("commit doesnt match. got height %d, expected %d", commitHeight, height+1)) 1096 } 1097 blocks = append(blocks, block) 1098 commits = append(commits, thisBlockCommit) 1099 height++ 1100 } 1101 case *types.PartSetHeader: 1102 thisBlockParts = types.NewPartSetFromHeader(*p) 1103 case *types.Part: 1104 _, err := thisBlockParts.AddPart(p) 1105 if err != nil { 1106 return nil, nil, err 1107 } 1108 case *types.Vote: 1109 if p.Type == tmproto.PrecommitType { 1110 thisBlockCommit = types.NewCommit(p.Height, p.Round, 1111 p.BlockID, []types.CommitSig{p.CommitSig()}) 1112 } 1113 } 1114 } 1115 // grab the last block too 1116 bz, err := io.ReadAll(thisBlockParts.GetReader()) 1117 if err != nil { 1118 panic(err) 1119 } 1120 pbb := new(tmproto.Block) 1121 err = proto.Unmarshal(bz, pbb) 1122 if err != nil { 1123 panic(err) 1124 } 1125 block, err := types.BlockFromProto(pbb) 1126 if err != nil { 1127 panic(err) 1128 } 1129 if block.Height != height+1 { 1130 panic(fmt.Sprintf("read bad block from wal. got height %d, expected %d", block.Height, height+1)) 1131 } 1132 commitHeight := thisBlockCommit.Height 1133 if commitHeight != height+1 { 1134 panic(fmt.Sprintf("commit doesnt match. got height %d, expected %d", commitHeight, height+1)) 1135 } 1136 blocks = append(blocks, block) 1137 commits = append(commits, thisBlockCommit) 1138 return blocks, commits, nil 1139 } 1140 1141 func readPieceFromWAL(msg *TimedWALMessage) interface{} { 1142 // for logging 1143 switch m := msg.Msg.(type) { 1144 case msgInfo: 1145 switch msg := m.Msg.(type) { 1146 case *ProposalMessage: 1147 return &msg.Proposal.BlockID.PartSetHeader 1148 case *BlockPartMessage: 1149 return msg.Part 1150 case *VoteMessage: 1151 return msg.Vote 1152 } 1153 case EndHeightMessage: 1154 return m 1155 } 1156 1157 return nil 1158 } 1159 1160 // fresh state and mock store 1161 func stateAndStore( 1162 config *cfg.Config, 1163 pubKey crypto.PubKey, 1164 appVersion uint64, 1165 ) (dbm.DB, sm.State, *mockBlockStore) { 1166 stateDB := dbm.NewMemDB() 1167 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 1168 DiscardABCIResponses: false, 1169 }) 1170 state, _ := sm.MakeGenesisStateFromFile(config.GenesisFile()) 1171 state.Version.Consensus.App = appVersion 1172 store := newMockBlockStore(config, state.ConsensusParams) 1173 if err := stateStore.Save(state); err != nil { 1174 panic(err) 1175 } 1176 return stateDB, state, store 1177 } 1178 1179 //---------------------------------- 1180 // mock block store 1181 1182 type mockBlockStore struct { 1183 config *cfg.Config 1184 params tmproto.ConsensusParams 1185 chain []*types.Block 1186 commits []*types.Commit 1187 base int64 1188 } 1189 1190 // TODO: NewBlockStore(db.NewMemDB) ... 1191 func newMockBlockStore(config *cfg.Config, params tmproto.ConsensusParams) *mockBlockStore { 1192 return &mockBlockStore{config, params, nil, nil, 0} 1193 } 1194 1195 func (bs *mockBlockStore) Height() int64 { return int64(len(bs.chain)) } 1196 func (bs *mockBlockStore) Base() int64 { return bs.base } 1197 func (bs *mockBlockStore) Size() int64 { return bs.Height() - bs.Base() + 1 } 1198 func (bs *mockBlockStore) LoadBaseMeta() *types.BlockMeta { return bs.LoadBlockMeta(bs.base) } 1199 func (bs *mockBlockStore) LoadBlock(height int64) *types.Block { return bs.chain[height-1] } 1200 func (bs *mockBlockStore) LoadBlockByHash(hash []byte) *types.Block { 1201 return bs.chain[int64(len(bs.chain))-1] 1202 } 1203 1204 func (bs *mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta { 1205 block := bs.chain[height-1] 1206 return &types.BlockMeta{ 1207 BlockID: types.BlockID{Hash: block.Hash(), PartSetHeader: block.MakePartSet(types.BlockPartSizeBytes).Header()}, 1208 Header: block.Header, 1209 } 1210 } 1211 func (bs *mockBlockStore) LoadBlockPart(height int64, index int) *types.Part { return nil } 1212 func (bs *mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { 1213 } 1214 1215 func (bs *mockBlockStore) LoadBlockCommit(height int64) *types.Commit { 1216 return bs.commits[height-1] 1217 } 1218 1219 func (bs *mockBlockStore) LoadSeenCommit(height int64) *types.Commit { 1220 return bs.commits[height-1] 1221 } 1222 1223 func (bs *mockBlockStore) PruneBlocks(height int64) (uint64, error) { 1224 pruned := uint64(0) 1225 for i := int64(0); i < height-1; i++ { 1226 bs.chain[i] = nil 1227 bs.commits[i] = nil 1228 pruned++ 1229 } 1230 bs.base = height 1231 return pruned, nil 1232 } 1233 1234 //--------------------------------------- 1235 // Test handshake/init chain 1236 1237 func TestHandshakeUpdatesValidators(t *testing.T) { 1238 val, _ := types.RandValidator(true, 10) 1239 vals := types.NewValidatorSet([]*types.Validator{val}) 1240 app := &initChainApp{vals: types.TM2PB.ValidatorUpdates(vals)} 1241 clientCreator := proxy.NewLocalClientCreator(app) 1242 1243 config := ResetConfig("handshake_test_") 1244 defer os.RemoveAll(config.RootDir) 1245 privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) 1246 pubKey, err := privVal.GetPubKey() 1247 require.NoError(t, err) 1248 stateDB, state, store := stateAndStore(config, pubKey, 0x0) 1249 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 1250 DiscardABCIResponses: false, 1251 }) 1252 1253 oldValAddr := state.Validators.Validators[0].Address 1254 1255 // now start the app using the handshake - it should sync 1256 genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) 1257 handshaker := NewHandshaker(stateStore, state, store, genDoc) 1258 proxyApp := proxy.NewAppConns(clientCreator) 1259 if err := proxyApp.Start(); err != nil { 1260 t.Fatalf("Error starting proxy app connections: %v", err) 1261 } 1262 t.Cleanup(func() { 1263 if err := proxyApp.Stop(); err != nil { 1264 t.Error(err) 1265 } 1266 }) 1267 if err := handshaker.Handshake(proxyApp); err != nil { 1268 t.Fatalf("Error on abci handshake: %v", err) 1269 } 1270 // reload the state, check the validator set was updated 1271 state, err = stateStore.Load() 1272 require.NoError(t, err) 1273 1274 newValAddr := state.Validators.Validators[0].Address 1275 expectValAddr := val.Address 1276 assert.NotEqual(t, oldValAddr, newValAddr) 1277 assert.Equal(t, newValAddr, expectValAddr) 1278 } 1279 1280 // returns the vals on InitChain 1281 type initChainApp struct { 1282 abci.BaseApplication 1283 vals []abci.ValidatorUpdate 1284 } 1285 1286 func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain { 1287 return abci.ResponseInitChain{ 1288 Validators: ica.vals, 1289 } 1290 }