github.com/vipernet-xyz/tm@v0.34.24/blockchain/v2/reactor_test.go (about)

     1  package v2
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"os"
     7  	"sort"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/gogo/protobuf/proto"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  	dbm "github.com/tendermint/tm-db"
    16  
    17  	abci "github.com/vipernet-xyz/tm/abci/types"
    18  	"github.com/vipernet-xyz/tm/behaviour"
    19  	cfg "github.com/vipernet-xyz/tm/config"
    20  	"github.com/vipernet-xyz/tm/libs/log"
    21  	"github.com/vipernet-xyz/tm/libs/service"
    22  	"github.com/vipernet-xyz/tm/mempool/mock"
    23  	"github.com/vipernet-xyz/tm/p2p"
    24  	"github.com/vipernet-xyz/tm/p2p/conn"
    25  	bcproto "github.com/vipernet-xyz/tm/proto/tendermint/blockchain"
    26  	"github.com/vipernet-xyz/tm/proxy"
    27  	sm "github.com/vipernet-xyz/tm/state"
    28  	"github.com/vipernet-xyz/tm/store"
    29  	"github.com/vipernet-xyz/tm/types"
    30  	tmtime "github.com/vipernet-xyz/tm/types/time"
    31  )
    32  
    33  type mockPeer struct {
    34  	service.Service
    35  	id p2p.ID
    36  }
    37  
    38  func (mp mockPeer) FlushStop()           {}
    39  func (mp mockPeer) ID() p2p.ID           { return mp.id }
    40  func (mp mockPeer) RemoteIP() net.IP     { return net.IP{} }
    41  func (mp mockPeer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.RemoteIP(), Port: 8800} }
    42  
    43  func (mp mockPeer) IsOutbound() bool   { return true }
    44  func (mp mockPeer) IsPersistent() bool { return true }
    45  func (mp mockPeer) CloseConn() error   { return nil }
    46  
    47  func (mp mockPeer) NodeInfo() p2p.NodeInfo {
    48  	return p2p.DefaultNodeInfo{
    49  		DefaultNodeID: "",
    50  		ListenAddr:    "",
    51  	}
    52  }
    53  func (mp mockPeer) Status() conn.ConnectionStatus { return conn.ConnectionStatus{} }
    54  func (mp mockPeer) SocketAddr() *p2p.NetAddress   { return &p2p.NetAddress{} }
    55  
    56  func (mp mockPeer) SendEnvelope(e p2p.Envelope) bool    { return true }
    57  func (mp mockPeer) TrySendEnvelope(e p2p.Envelope) bool { return true }
    58  
    59  func (mp mockPeer) Send(byte, []byte) bool    { return true }
    60  func (mp mockPeer) TrySend(byte, []byte) bool { return true }
    61  
    62  func (mp mockPeer) Set(string, interface{}) {}
    63  func (mp mockPeer) Get(string) interface{}  { return struct{}{} }
    64  
    65  func (mp mockPeer) SetRemovalFailed()      {}
    66  func (mp mockPeer) GetRemovalFailed() bool { return false }
    67  
    68  type mockBlockApplier struct{}
    69  
    70  // XXX: Add whitelist/blacklist?
    71  func (mba *mockBlockApplier) ApplyBlock(
    72  	state sm.State, blockID types.BlockID, block *types.Block,
    73  ) (sm.State, int64, error) {
    74  	state.LastBlockHeight++
    75  	return state, 0, nil
    76  }
    77  
    78  type mockSwitchIo struct {
    79  	mtx                 sync.Mutex
    80  	switchedToConsensus bool
    81  	numStatusResponse   int
    82  	numBlockResponse    int
    83  	numNoBlockResponse  int
    84  }
    85  
    86  func (sio *mockSwitchIo) sendBlockRequest(peerID p2p.ID, height int64) error {
    87  	return nil
    88  }
    89  
    90  func (sio *mockSwitchIo) sendStatusResponse(base, height int64, peerID p2p.ID) error {
    91  	sio.mtx.Lock()
    92  	defer sio.mtx.Unlock()
    93  	sio.numStatusResponse++
    94  	return nil
    95  }
    96  
    97  func (sio *mockSwitchIo) sendBlockToPeer(block *types.Block, peerID p2p.ID) error {
    98  	sio.mtx.Lock()
    99  	defer sio.mtx.Unlock()
   100  	sio.numBlockResponse++
   101  	return nil
   102  }
   103  
   104  func (sio *mockSwitchIo) sendBlockNotFound(height int64, peerID p2p.ID) error {
   105  	sio.mtx.Lock()
   106  	defer sio.mtx.Unlock()
   107  	sio.numNoBlockResponse++
   108  	return nil
   109  }
   110  
   111  func (sio *mockSwitchIo) trySwitchToConsensus(state sm.State, skipWAL bool) bool {
   112  	sio.mtx.Lock()
   113  	defer sio.mtx.Unlock()
   114  	sio.switchedToConsensus = true
   115  	return true
   116  }
   117  
   118  func (sio *mockSwitchIo) broadcastStatusRequest() {
   119  }
   120  
   121  type testReactorParams struct {
   122  	logger      log.Logger
   123  	genDoc      *types.GenesisDoc
   124  	privVals    []types.PrivValidator
   125  	startHeight int64
   126  	mockA       bool
   127  }
   128  
   129  func newTestReactor(p testReactorParams) *BlockchainReactor {
   130  	store, state, _ := newReactorStore(p.genDoc, p.privVals, p.startHeight)
   131  	reporter := behaviour.NewMockReporter()
   132  
   133  	var appl blockApplier
   134  
   135  	if p.mockA {
   136  		appl = &mockBlockApplier{}
   137  	} else {
   138  		app := &testApp{}
   139  		cc := proxy.NewLocalClientCreator(app)
   140  		proxyApp := proxy.NewAppConns(cc)
   141  		err := proxyApp.Start()
   142  		if err != nil {
   143  			panic(fmt.Errorf("error start app: %w", err))
   144  		}
   145  		db := dbm.NewMemDB()
   146  		stateStore := sm.NewStore(db, sm.StoreOptions{
   147  			DiscardABCIResponses: false,
   148  		})
   149  		appl = sm.NewBlockExecutor(stateStore, p.logger, proxyApp.Consensus(), mock.Mempool{}, sm.EmptyEvidencePool{})
   150  		if err = stateStore.Save(state); err != nil {
   151  			panic(err)
   152  		}
   153  	}
   154  
   155  	r := newReactor(state, store, reporter, appl, true)
   156  	logger := log.TestingLogger()
   157  	r.SetLogger(logger.With("module", "blockchain"))
   158  
   159  	return r
   160  }
   161  
   162  // This test is left here and not deleted to retain the termination cases for
   163  // future improvement in [#4482](https://github.com/vipernet-xyz/tm/issues/4482).
   164  // func TestReactorTerminationScenarios(t *testing.T) {
   165  
   166  // 	config := cfg.ResetTestRoot("blockchain_reactor_v2_test")
   167  // 	defer os.RemoveAll(config.RootDir)
   168  // 	genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30)
   169  // 	refStore, _, _ := newReactorStore(genDoc, privVals, 20)
   170  
   171  // 	params := testReactorParams{
   172  // 		logger:      log.TestingLogger(),
   173  // 		genDoc:      genDoc,
   174  // 		privVals:    privVals,
   175  // 		startHeight: 10,
   176  // 		bufferSize:  100,
   177  // 		mockA:       true,
   178  // 	}
   179  
   180  // 	type testEvent struct {
   181  // 		evType string
   182  // 		peer   string
   183  // 		height int64
   184  // 	}
   185  
   186  // 	tests := []struct {
   187  // 		name   string
   188  // 		params testReactorParams
   189  // 		msgs   []testEvent
   190  // 	}{
   191  // 		{
   192  // 			name:   "simple termination on max peer height - one peer",
   193  // 			params: params,
   194  // 			msgs: []testEvent{
   195  // 				{evType: "AddPeer", peer: "P1"},
   196  // 				{evType: "ReceiveS", peer: "P1", height: 13},
   197  // 				{evType: "BlockReq"},
   198  // 				{evType: "ReceiveB", peer: "P1", height: 11},
   199  // 				{evType: "BlockReq"},
   200  // 				{evType: "BlockReq"},
   201  // 				{evType: "ReceiveB", peer: "P1", height: 12},
   202  // 				{evType: "Process"},
   203  // 				{evType: "ReceiveB", peer: "P1", height: 13},
   204  // 				{evType: "Process"},
   205  // 			},
   206  // 		},
   207  // 		{
   208  // 			name:   "simple termination on max peer height - two peers",
   209  // 			params: params,
   210  // 			msgs: []testEvent{
   211  // 				{evType: "AddPeer", peer: "P1"},
   212  // 				{evType: "AddPeer", peer: "P2"},
   213  // 				{evType: "ReceiveS", peer: "P1", height: 13},
   214  // 				{evType: "ReceiveS", peer: "P2", height: 15},
   215  // 				{evType: "BlockReq"},
   216  // 				{evType: "BlockReq"},
   217  // 				{evType: "ReceiveB", peer: "P1", height: 11},
   218  // 				{evType: "ReceiveB", peer: "P2", height: 12},
   219  // 				{evType: "Process"},
   220  // 				{evType: "BlockReq"},
   221  // 				{evType: "BlockReq"},
   222  // 				{evType: "ReceiveB", peer: "P1", height: 13},
   223  // 				{evType: "Process"},
   224  // 				{evType: "ReceiveB", peer: "P2", height: 14},
   225  // 				{evType: "Process"},
   226  // 				{evType: "BlockReq"},
   227  // 				{evType: "ReceiveB", peer: "P2", height: 15},
   228  // 				{evType: "Process"},
   229  // 			},
   230  // 		},
   231  // 		{
   232  // 			name:   "termination on max peer height - two peers, noBlock error",
   233  // 			params: params,
   234  // 			msgs: []testEvent{
   235  // 				{evType: "AddPeer", peer: "P1"},
   236  // 				{evType: "AddPeer", peer: "P2"},
   237  // 				{evType: "ReceiveS", peer: "P1", height: 13},
   238  // 				{evType: "ReceiveS", peer: "P2", height: 15},
   239  // 				{evType: "BlockReq"},
   240  // 				{evType: "BlockReq"},
   241  // 				{evType: "ReceiveNB", peer: "P1", height: 11},
   242  // 				{evType: "BlockReq"},
   243  // 				{evType: "ReceiveB", peer: "P2", height: 12},
   244  // 				{evType: "ReceiveB", peer: "P2", height: 11},
   245  // 				{evType: "Process"},
   246  // 				{evType: "BlockReq"},
   247  // 				{evType: "BlockReq"},
   248  // 				{evType: "ReceiveB", peer: "P2", height: 13},
   249  // 				{evType: "Process"},
   250  // 				{evType: "ReceiveB", peer: "P2", height: 14},
   251  // 				{evType: "Process"},
   252  // 				{evType: "BlockReq"},
   253  // 				{evType: "ReceiveB", peer: "P2", height: 15},
   254  // 				{evType: "Process"},
   255  // 			},
   256  // 		},
   257  // 		{
   258  // 			name:   "termination on max peer height - two peers, remove one peer",
   259  // 			params: params,
   260  // 			msgs: []testEvent{
   261  // 				{evType: "AddPeer", peer: "P1"},
   262  // 				{evType: "AddPeer", peer: "P2"},
   263  // 				{evType: "ReceiveS", peer: "P1", height: 13},
   264  // 				{evType: "ReceiveS", peer: "P2", height: 15},
   265  // 				{evType: "BlockReq"},
   266  // 				{evType: "BlockReq"},
   267  // 				{evType: "RemovePeer", peer: "P1"},
   268  // 				{evType: "BlockReq"},
   269  // 				{evType: "ReceiveB", peer: "P2", height: 12},
   270  // 				{evType: "ReceiveB", peer: "P2", height: 11},
   271  // 				{evType: "Process"},
   272  // 				{evType: "BlockReq"},
   273  // 				{evType: "BlockReq"},
   274  // 				{evType: "ReceiveB", peer: "P2", height: 13},
   275  // 				{evType: "Process"},
   276  // 				{evType: "ReceiveB", peer: "P2", height: 14},
   277  // 				{evType: "Process"},
   278  // 				{evType: "BlockReq"},
   279  // 				{evType: "ReceiveB", peer: "P2", height: 15},
   280  // 				{evType: "Process"},
   281  // 			},
   282  // 		},
   283  // 	}
   284  
   285  // 	for _, tt := range tests {
   286  // 		tt := tt
   287  // 		t.Run(tt.name, func(t *testing.T) {
   288  // 			reactor := newTestReactor(params)
   289  // 			reactor.Start()
   290  // 			reactor.reporter = behaviour.NewMockReporter()
   291  // 			mockSwitch := &mockSwitchIo{switchedToConsensus: false}
   292  // 			reactor.io = mockSwitch
   293  // 			// time for go routines to start
   294  // 			time.Sleep(time.Millisecond)
   295  
   296  // 			for _, step := range tt.msgs {
   297  // 				switch step.evType {
   298  // 				case "AddPeer":
   299  // 					reactor.scheduler.send(bcAddNewPeer{peerID: p2p.ID(step.peer)})
   300  // 				case "RemovePeer":
   301  // 					reactor.scheduler.send(bcRemovePeer{peerID: p2p.ID(step.peer)})
   302  // 				case "ReceiveS":
   303  // 					reactor.scheduler.send(bcStatusResponse{
   304  // 						peerID: p2p.ID(step.peer),
   305  // 						height: step.height,
   306  // 						time:   time.Now(),
   307  // 					})
   308  // 				case "ReceiveB":
   309  // 					reactor.scheduler.send(bcBlockResponse{
   310  // 						peerID: p2p.ID(step.peer),
   311  // 						block:  refStore.LoadBlock(step.height),
   312  // 						size:   10,
   313  // 						time:   time.Now(),
   314  // 					})
   315  // 				case "ReceiveNB":
   316  // 					reactor.scheduler.send(bcNoBlockResponse{
   317  // 						peerID: p2p.ID(step.peer),
   318  // 						height: step.height,
   319  // 						time:   time.Now(),
   320  // 					})
   321  // 				case "BlockReq":
   322  // 					reactor.scheduler.send(rTrySchedule{time: time.Now()})
   323  // 				case "Process":
   324  // 					reactor.processor.send(rProcessBlock{})
   325  // 				}
   326  // 				// give time for messages to propagate between routines
   327  // 				time.Sleep(time.Millisecond)
   328  // 			}
   329  
   330  // 			// time for processor to finish and reactor to switch to consensus
   331  // 			time.Sleep(20 * time.Millisecond)
   332  // 			assert.True(t, mockSwitch.hasSwitchedToConsensus())
   333  // 			reactor.Stop()
   334  // 		})
   335  // 	}
   336  // }
   337  
   338  func TestReactorHelperMode(t *testing.T) {
   339  	channelID := byte(0x40)
   340  
   341  	config := cfg.ResetTestRoot("blockchain_reactor_v2_test")
   342  	defer os.RemoveAll(config.RootDir)
   343  	genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30)
   344  
   345  	params := testReactorParams{
   346  		logger:      log.TestingLogger(),
   347  		genDoc:      genDoc,
   348  		privVals:    privVals,
   349  		startHeight: 20,
   350  		mockA:       true,
   351  	}
   352  
   353  	type testEvent struct {
   354  		peer  string
   355  		event proto.Message
   356  	}
   357  
   358  	tests := []struct {
   359  		name   string
   360  		params testReactorParams
   361  		msgs   []testEvent
   362  	}{
   363  		{
   364  			name:   "status request",
   365  			params: params,
   366  			msgs: []testEvent{
   367  				{"P1", &bcproto.StatusRequest{}},
   368  				{"P1", &bcproto.BlockRequest{Height: 13}},
   369  				{"P1", &bcproto.BlockRequest{Height: 20}},
   370  				{"P1", &bcproto.BlockRequest{Height: 22}},
   371  			},
   372  		},
   373  	}
   374  
   375  	for _, tt := range tests {
   376  		tt := tt
   377  		t.Run(tt.name, func(t *testing.T) {
   378  			reactor := newTestReactor(params)
   379  			mockSwitch := &mockSwitchIo{switchedToConsensus: false}
   380  			reactor.io = mockSwitch
   381  			err := reactor.Start()
   382  			require.NoError(t, err)
   383  
   384  			for i := 0; i < len(tt.msgs); i++ {
   385  				step := tt.msgs[i]
   386  				switch ev := step.event.(type) {
   387  				case *bcproto.StatusRequest:
   388  					old := mockSwitch.numStatusResponse
   389  					reactor.ReceiveEnvelope(p2p.Envelope{
   390  						ChannelID: channelID,
   391  						Src:       mockPeer{id: p2p.ID(step.peer)},
   392  						Message:   ev})
   393  					assert.Equal(t, old+1, mockSwitch.numStatusResponse)
   394  				case *bcproto.BlockRequest:
   395  					if ev.Height > params.startHeight {
   396  						old := mockSwitch.numNoBlockResponse
   397  						reactor.ReceiveEnvelope(p2p.Envelope{
   398  							ChannelID: channelID,
   399  							Src:       mockPeer{id: p2p.ID(step.peer)},
   400  							Message:   ev})
   401  						assert.Equal(t, old+1, mockSwitch.numNoBlockResponse)
   402  					} else {
   403  						old := mockSwitch.numBlockResponse
   404  						reactor.ReceiveEnvelope(p2p.Envelope{
   405  							ChannelID: channelID,
   406  							Src:       mockPeer{id: p2p.ID(step.peer)},
   407  							Message:   ev})
   408  						assert.Equal(t, old+1, mockSwitch.numBlockResponse)
   409  					}
   410  				}
   411  			}
   412  			err = reactor.Stop()
   413  			require.NoError(t, err)
   414  		})
   415  	}
   416  }
   417  
   418  func TestLegacyReactorReceiveBasic(t *testing.T) {
   419  	config := cfg.ResetTestRoot("blockchain_reactor_v2_test")
   420  	defer os.RemoveAll(config.RootDir)
   421  	genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30)
   422  	params := testReactorParams{
   423  		logger:      log.TestingLogger(),
   424  		genDoc:      genDoc,
   425  		privVals:    privVals,
   426  		startHeight: 20,
   427  		mockA:       true,
   428  	}
   429  	reactor := newTestReactor(params)
   430  	mockSwitch := &mockSwitchIo{switchedToConsensus: false}
   431  	reactor.io = mockSwitch
   432  	peer := p2p.CreateRandomPeer(false)
   433  
   434  	reactor.InitPeer(peer)
   435  	reactor.AddPeer(peer)
   436  	m := &bcproto.StatusRequest{}
   437  	wm := m.Wrap()
   438  	msg, err := proto.Marshal(wm)
   439  	assert.NoError(t, err)
   440  
   441  	assert.NotPanics(t, func() {
   442  		reactor.Receive(BlockchainChannel, peer, msg)
   443  	})
   444  }
   445  
   446  func TestReactorSetSwitchNil(t *testing.T) {
   447  	config := cfg.ResetTestRoot("blockchain_reactor_v2_test")
   448  	defer os.RemoveAll(config.RootDir)
   449  	genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30)
   450  
   451  	reactor := newTestReactor(testReactorParams{
   452  		logger:   log.TestingLogger(),
   453  		genDoc:   genDoc,
   454  		privVals: privVals,
   455  	})
   456  	reactor.SetSwitch(nil)
   457  
   458  	assert.Nil(t, reactor.Switch)
   459  	assert.Nil(t, reactor.io)
   460  }
   461  
   462  //----------------------------------------------
   463  // utility funcs
   464  
   465  func makeTxs(height int64) (txs []types.Tx) {
   466  	for i := 0; i < 10; i++ {
   467  		txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
   468  	}
   469  	return txs
   470  }
   471  
   472  func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block {
   473  	block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address)
   474  	return block
   475  }
   476  
   477  type testApp struct {
   478  	abci.BaseApplication
   479  }
   480  
   481  func randGenesisDoc(chainID string, numValidators int, randPower bool, minPower int64) (
   482  	*types.GenesisDoc, []types.PrivValidator,
   483  ) {
   484  	validators := make([]types.GenesisValidator, numValidators)
   485  	privValidators := make([]types.PrivValidator, numValidators)
   486  	for i := 0; i < numValidators; i++ {
   487  		val, privVal := types.RandValidator(randPower, minPower)
   488  		validators[i] = types.GenesisValidator{
   489  			PubKey: val.PubKey,
   490  			Power:  val.VotingPower,
   491  		}
   492  		privValidators[i] = privVal
   493  	}
   494  	sort.Sort(types.PrivValidatorsByAddress(privValidators))
   495  
   496  	return &types.GenesisDoc{
   497  		GenesisTime: tmtime.Now(),
   498  		ChainID:     chainID,
   499  		Validators:  validators,
   500  	}, privValidators
   501  }
   502  
   503  // Why are we importing the entire blockExecutor dependency graph here
   504  // when we have the facilities to
   505  func newReactorStore(
   506  	genDoc *types.GenesisDoc,
   507  	privVals []types.PrivValidator,
   508  	maxBlockHeight int64,
   509  ) (*store.BlockStore, sm.State, *sm.BlockExecutor) {
   510  	if len(privVals) != 1 {
   511  		panic("only support one validator")
   512  	}
   513  	app := &testApp{}
   514  	cc := proxy.NewLocalClientCreator(app)
   515  	proxyApp := proxy.NewAppConns(cc)
   516  	err := proxyApp.Start()
   517  	if err != nil {
   518  		panic(fmt.Errorf("error start app: %w", err))
   519  	}
   520  
   521  	stateDB := dbm.NewMemDB()
   522  	blockStore := store.NewBlockStore(dbm.NewMemDB())
   523  	stateStore := sm.NewStore(stateDB, sm.StoreOptions{
   524  		DiscardABCIResponses: false,
   525  	})
   526  	state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc)
   527  	if err != nil {
   528  		panic(fmt.Errorf("error constructing state from genesis file: %w", err))
   529  	}
   530  
   531  	db := dbm.NewMemDB()
   532  	stateStore = sm.NewStore(db, sm.StoreOptions{
   533  		DiscardABCIResponses: false,
   534  	},
   535  	)
   536  	blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
   537  		mock.Mempool{}, sm.EmptyEvidencePool{})
   538  	if err = stateStore.Save(state); err != nil {
   539  		panic(err)
   540  	}
   541  
   542  	// add blocks in
   543  	for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
   544  		lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil)
   545  		if blockHeight > 1 {
   546  			lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1)
   547  			lastBlock := blockStore.LoadBlock(blockHeight - 1)
   548  			vote, err := types.MakeVote(
   549  				lastBlock.Header.Height,
   550  				lastBlockMeta.BlockID,
   551  				state.Validators,
   552  				privVals[0],
   553  				lastBlock.Header.ChainID,
   554  				time.Now(),
   555  			)
   556  			if err != nil {
   557  				panic(err)
   558  			}
   559  			lastCommit = types.NewCommit(vote.Height, vote.Round,
   560  				lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()})
   561  		}
   562  
   563  		thisBlock := makeBlock(blockHeight, state, lastCommit)
   564  
   565  		thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes)
   566  		blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()}
   567  
   568  		state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock)
   569  		if err != nil {
   570  			panic(fmt.Errorf("error apply block: %w", err))
   571  		}
   572  
   573  		blockStore.SaveBlock(thisBlock, thisParts, lastCommit)
   574  	}
   575  	return blockStore, state, blockExec
   576  }