github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/node/node_test.go (about)

     1  package node
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math"
     7  	"net"
     8  	"os"
     9  	"syscall"
    10  	"testing"
    11  	"time"
    12  
    13  	mdutils "github.com/ipfs/go-merkledag/test"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  
    17  	"github.com/lazyledger/lazyledger-core/abci/example/kvstore"
    18  	cfg "github.com/lazyledger/lazyledger-core/config"
    19  	"github.com/lazyledger/lazyledger-core/crypto"
    20  	"github.com/lazyledger/lazyledger-core/crypto/ed25519"
    21  	"github.com/lazyledger/lazyledger-core/crypto/tmhash"
    22  	"github.com/lazyledger/lazyledger-core/evidence"
    23  	"github.com/lazyledger/lazyledger-core/ipfs"
    24  	dbm "github.com/lazyledger/lazyledger-core/libs/db"
    25  	"github.com/lazyledger/lazyledger-core/libs/db/memdb"
    26  	"github.com/lazyledger/lazyledger-core/libs/log"
    27  	tmrand "github.com/lazyledger/lazyledger-core/libs/rand"
    28  	mempl "github.com/lazyledger/lazyledger-core/mempool"
    29  	"github.com/lazyledger/lazyledger-core/p2p"
    30  	p2pmock "github.com/lazyledger/lazyledger-core/p2p/mock"
    31  	"github.com/lazyledger/lazyledger-core/privval"
    32  	"github.com/lazyledger/lazyledger-core/proxy"
    33  	sm "github.com/lazyledger/lazyledger-core/state"
    34  	"github.com/lazyledger/lazyledger-core/store"
    35  	"github.com/lazyledger/lazyledger-core/types"
    36  	tmtime "github.com/lazyledger/lazyledger-core/types/time"
    37  )
    38  
    39  func defaultNewTestNode(config *cfg.Config, logger log.Logger) (*Node, error) {
    40  	nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
    41  	if err != nil {
    42  		return nil, fmt.Errorf("failed to load or gen node key %s: %w", config.NodeKeyFile(), err)
    43  	}
    44  
    45  	pval, err := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	return NewNode(config,
    51  		pval,
    52  		nodeKey,
    53  		proxy.DefaultClientCreator(config.ProxyApp, config.DBDir()),
    54  		DefaultGenesisDocProviderFunc(config),
    55  		InMemDBProvider,
    56  		ipfs.Mock(),
    57  		DefaultMetricsProvider(config.Instrumentation),
    58  		logger,
    59  	)
    60  }
    61  
    62  func TestNodeStartStop(t *testing.T) {
    63  	config := cfg.ResetTestRoot("node_node_test")
    64  	defer os.RemoveAll(config.RootDir)
    65  
    66  	// create & start node
    67  	n, err := defaultNewTestNode(config, log.TestingLogger())
    68  	require.NoError(t, err)
    69  	err = n.Start()
    70  	require.NoError(t, err)
    71  
    72  	t.Logf("Started node %v", n.sw.NodeInfo())
    73  
    74  	// wait for the node to produce a block
    75  	blocksSub, err := n.EventBus().Subscribe(context.Background(), "node_test", types.EventQueryNewBlock)
    76  	require.NoError(t, err)
    77  	select {
    78  	case <-blocksSub.Out():
    79  	case <-blocksSub.Cancelled():
    80  		t.Fatal("blocksSub was cancelled")
    81  	case <-time.After(10 * time.Second):
    82  		t.Fatal("timed out waiting for the node to produce a block")
    83  	}
    84  
    85  	// stop the node
    86  	go func() {
    87  		err = n.Stop()
    88  		require.NoError(t, err)
    89  	}()
    90  
    91  	select {
    92  	case <-n.Quit():
    93  	case <-time.After(10 * time.Second):
    94  		pid := os.Getpid()
    95  		p, err := os.FindProcess(pid)
    96  		if err != nil {
    97  			panic(err)
    98  		}
    99  		err = p.Signal(syscall.SIGABRT)
   100  		fmt.Println(err)
   101  		t.Fatal("timed out waiting for shutdown")
   102  	}
   103  }
   104  
   105  func TestSplitAndTrimEmpty(t *testing.T) {
   106  	testCases := []struct {
   107  		s        string
   108  		sep      string
   109  		cutset   string
   110  		expected []string
   111  	}{
   112  		{"a,b,c", ",", " ", []string{"a", "b", "c"}},
   113  		{" a , b , c ", ",", " ", []string{"a", "b", "c"}},
   114  		{" a, b, c ", ",", " ", []string{"a", "b", "c"}},
   115  		{" a, ", ",", " ", []string{"a"}},
   116  		{"   ", ",", " ", []string{}},
   117  	}
   118  
   119  	for _, tc := range testCases {
   120  		assert.Equal(t, tc.expected, splitAndTrimEmpty(tc.s, tc.sep, tc.cutset), "%s", tc.s)
   121  	}
   122  }
   123  
   124  func TestNodeDelayedStart(t *testing.T) {
   125  	config := cfg.ResetTestRoot("node_delayed_start_test")
   126  	defer os.RemoveAll(config.RootDir)
   127  	now := tmtime.Now()
   128  
   129  	// create & start node
   130  	n, err := defaultNewTestNode(config, log.TestingLogger())
   131  	n.GenesisDoc().GenesisTime = now.Add(2 * time.Second)
   132  	require.NoError(t, err)
   133  
   134  	err = n.Start()
   135  	require.NoError(t, err)
   136  	defer n.Stop() //nolint:errcheck // ignore for tests
   137  
   138  	startTime := tmtime.Now()
   139  	assert.Equal(t, true, startTime.After(n.GenesisDoc().GenesisTime))
   140  }
   141  
   142  func TestNodeSetAppVersion(t *testing.T) {
   143  	config := cfg.ResetTestRoot("node_app_version_test")
   144  	defer os.RemoveAll(config.RootDir)
   145  
   146  	// create & start node
   147  	n, err := defaultNewTestNode(config, log.TestingLogger())
   148  	require.NoError(t, err)
   149  
   150  	// default config uses the kvstore app
   151  	var appVersion uint64 = kvstore.ProtocolVersion
   152  
   153  	// check version is set in state
   154  	state, err := n.stateStore.Load()
   155  	require.NoError(t, err)
   156  	assert.Equal(t, state.Version.Consensus.App, appVersion)
   157  
   158  	// check version is set in node info
   159  	assert.Equal(t, n.nodeInfo.(p2p.DefaultNodeInfo).ProtocolVersion.App, appVersion)
   160  }
   161  
   162  func TestNodeSetPrivValTCP(t *testing.T) {
   163  	t.Skip("TODO(ismail): Mock these conns using net.Pipe instead")
   164  	addr := "tcp://" + testFreeAddr(t)
   165  
   166  	config := cfg.ResetTestRoot("node_priv_val_tcp_test")
   167  	defer os.RemoveAll(config.RootDir)
   168  	config.BaseConfig.PrivValidatorListenAddr = addr
   169  
   170  	dialer := privval.DialTCPFn(addr, 100*time.Millisecond, ed25519.GenPrivKey())
   171  	dialerEndpoint := privval.NewSignerDialerEndpoint(
   172  		log.TestingLogger(),
   173  		dialer,
   174  	)
   175  	privval.SignerDialerEndpointTimeoutReadWrite(100 * time.Millisecond)(dialerEndpoint)
   176  
   177  	signerServer := privval.NewSignerServer(
   178  		dialerEndpoint,
   179  		config.ChainID(),
   180  		types.NewMockPV(),
   181  	)
   182  
   183  	go func() {
   184  		err := signerServer.Start()
   185  		if err != nil {
   186  			panic(err)
   187  		}
   188  	}()
   189  	defer signerServer.Stop() //nolint:errcheck // ignore for tests
   190  
   191  	logger := log.TestingLogger()
   192  	n, err := defaultNewTestNode(config, logger)
   193  	require.NoError(t, err)
   194  	assert.IsType(t, &privval.RetrySignerClient{}, n.PrivValidator())
   195  }
   196  
   197  // address without a protocol must result in error
   198  func TestPrivValidatorListenAddrNoProtocol(t *testing.T) {
   199  	addrNoPrefix := testFreeAddr(t)
   200  
   201  	config := cfg.ResetTestRoot("node_priv_val_tcp_test")
   202  	defer os.RemoveAll(config.RootDir)
   203  	config.BaseConfig.PrivValidatorListenAddr = addrNoPrefix
   204  
   205  	_, err := defaultNewTestNode(config, log.TestingLogger())
   206  	assert.Error(t, err)
   207  }
   208  
   209  func TestNodeSetPrivValIPC(t *testing.T) {
   210  	tmpfile := "/tmp/kms." + tmrand.Str(6) + ".sock"
   211  	defer os.Remove(tmpfile) // clean up
   212  
   213  	config := cfg.ResetTestRoot("node_priv_val_tcp_test")
   214  	defer os.RemoveAll(config.RootDir)
   215  	config.BaseConfig.PrivValidatorListenAddr = "unix://" + tmpfile
   216  
   217  	dialer := privval.DialUnixFn(tmpfile)
   218  	dialerEndpoint := privval.NewSignerDialerEndpoint(
   219  		log.TestingLogger(),
   220  		dialer,
   221  	)
   222  	privval.SignerDialerEndpointTimeoutReadWrite(400 * time.Millisecond)(dialerEndpoint)
   223  
   224  	pvsc := privval.NewSignerServer(
   225  		dialerEndpoint,
   226  		config.ChainID(),
   227  		types.NewMockPV(),
   228  	)
   229  
   230  	go func() {
   231  		err := pvsc.Start()
   232  		require.NoError(t, err)
   233  	}()
   234  	defer pvsc.Stop() //nolint:errcheck // ignore for tests
   235  
   236  	logger := log.TestingLogger()
   237  	n, err := defaultNewTestNode(config, logger)
   238  	require.NoError(t, err)
   239  	assert.IsType(t, &privval.RetrySignerClient{}, n.PrivValidator())
   240  }
   241  
   242  // testFreeAddr claims a free port so we don't block on listener being ready.
   243  func testFreeAddr(t *testing.T) string {
   244  	ln, err := net.Listen("tcp", "127.0.0.1:0")
   245  	require.NoError(t, err)
   246  	defer ln.Close()
   247  
   248  	return fmt.Sprintf("127.0.0.1:%d", ln.Addr().(*net.TCPAddr).Port)
   249  }
   250  
   251  // create a proposal block using real and full
   252  // mempool and evidence pool and validate it.
   253  func TestCreateProposalBlock(t *testing.T) {
   254  	config := cfg.ResetTestRoot("node_create_proposal")
   255  	defer os.RemoveAll(config.RootDir)
   256  	cc := proxy.NewLocalClientCreator(kvstore.NewApplication())
   257  	proxyApp := proxy.NewAppConns(cc)
   258  	err := proxyApp.Start()
   259  	require.Nil(t, err)
   260  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   261  
   262  	logger := log.TestingLogger()
   263  
   264  	const height int64 = 1
   265  	state, stateDB, privVals := state(1, height)
   266  	stateStore := sm.NewStore(stateDB)
   267  	maxBytes := 16384
   268  	const partSize uint32 = 256
   269  	maxEvidenceBytes := int64(maxBytes / 2)
   270  	state.ConsensusParams.Block.MaxBytes = int64(maxBytes)
   271  	state.ConsensusParams.Evidence.MaxBytes = maxEvidenceBytes
   272  	proposerAddr, _ := state.Validators.GetByIndex(0)
   273  
   274  	// Make Mempool
   275  	mempool := mempl.NewCListMempool(
   276  		config.Mempool,
   277  		proxyApp.Mempool(),
   278  		state.LastBlockHeight,
   279  		mempl.WithMetrics(mempl.NopMetrics()),
   280  		mempl.WithPreCheck(sm.TxPreCheck(state)),
   281  		mempl.WithPostCheck(sm.TxPostCheck(state)),
   282  	)
   283  	mempool.SetLogger(logger)
   284  
   285  	// Make EvidencePool
   286  	evidenceDB := memdb.NewDB()
   287  	blockStore := store.NewBlockStore(memdb.NewDB(), mdutils.Mock())
   288  	evidencePool, err := evidence.NewPool(evidenceDB, stateStore, blockStore)
   289  	require.NoError(t, err)
   290  	evidencePool.SetLogger(logger)
   291  
   292  	// fill the evidence pool with more evidence
   293  	// than can fit in a block
   294  	var currentBytes int64 = 0
   295  	for currentBytes <= maxEvidenceBytes {
   296  		ev := types.NewMockDuplicateVoteEvidenceWithValidator(height, time.Now(), privVals[0], "test-chain")
   297  		currentBytes += int64(len(ev.Bytes()))
   298  		err := evidencePool.AddEvidenceFromConsensus(ev)
   299  		require.NoError(t, err)
   300  	}
   301  
   302  	evList, size := evidencePool.PendingEvidence(state.ConsensusParams.Evidence.MaxBytes)
   303  	require.Less(t, size, state.ConsensusParams.Evidence.MaxBytes+1)
   304  	evData := &types.EvidenceData{Evidence: evList}
   305  	require.EqualValues(t, size, evData.ByteSize())
   306  
   307  	// fill the mempool with more txs
   308  	// than can fit in a block
   309  	txLength := 100
   310  	for i := 0; i <= maxBytes/txLength; i++ {
   311  		tx := tmrand.Bytes(txLength)
   312  		err := mempool.CheckTx(tx, nil, mempl.TxInfo{})
   313  		assert.NoError(t, err)
   314  	}
   315  
   316  	blockExec := sm.NewBlockExecutor(
   317  		stateStore,
   318  		logger,
   319  		proxyApp.Consensus(),
   320  		mempool,
   321  		evidencePool,
   322  	)
   323  
   324  	commit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
   325  	block, _ := blockExec.CreateProposalBlock(
   326  		height,
   327  		state, commit,
   328  		proposerAddr,
   329  	)
   330  
   331  	// check that the part set does not exceed the maximum block size
   332  	partSet := block.MakePartSet(partSize)
   333  	// TODO(ismail): properly fix this test
   334  	// https://github.com/lazyledger/lazyledger-core/issues/77
   335  	assert.Less(t, partSet.ByteSize(), int64(maxBytes)*2)
   336  
   337  	partSetFromHeader := types.NewPartSetFromHeader(partSet.Header())
   338  	for partSetFromHeader.Count() < partSetFromHeader.Total() {
   339  		added, err := partSetFromHeader.AddPart(partSet.GetPart(int(partSetFromHeader.Count())))
   340  		require.NoError(t, err)
   341  		require.True(t, added)
   342  	}
   343  	assert.EqualValues(t, partSetFromHeader.ByteSize(), partSet.ByteSize())
   344  
   345  	err = blockExec.ValidateBlock(state, block)
   346  	assert.NoError(t, err)
   347  }
   348  
   349  func TestMaxTxsProposalBlockSize(t *testing.T) {
   350  	config := cfg.ResetTestRoot("node_create_proposal")
   351  	defer os.RemoveAll(config.RootDir)
   352  	cc := proxy.NewLocalClientCreator(kvstore.NewApplication())
   353  	proxyApp := proxy.NewAppConns(cc)
   354  	err := proxyApp.Start()
   355  	require.Nil(t, err)
   356  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   357  
   358  	logger := log.TestingLogger()
   359  
   360  	const height int64 = 1
   361  	state, stateDB, _ := state(1, height)
   362  	stateStore := sm.NewStore(stateDB)
   363  	const maxBytes int64 = 16384
   364  	const partSize uint32 = 256
   365  	state.ConsensusParams.Block.MaxBytes = maxBytes
   366  	proposerAddr, _ := state.Validators.GetByIndex(0)
   367  
   368  	// Make Mempool
   369  	mempool := mempl.NewCListMempool(
   370  		config.Mempool,
   371  		proxyApp.Mempool(),
   372  		state.LastBlockHeight,
   373  		mempl.WithMetrics(mempl.NopMetrics()),
   374  		mempl.WithPreCheck(sm.TxPreCheck(state)),
   375  		mempl.WithPostCheck(sm.TxPostCheck(state)),
   376  	)
   377  	mempool.SetLogger(logger)
   378  
   379  	// fill the mempool with one txs just below the maximum size
   380  	txLength := int(types.MaxDataBytesNoEvidence(maxBytes, 1))
   381  	tx := tmrand.Bytes(txLength - 4 - 5) // to account for the varint
   382  	err = mempool.CheckTx(tx, nil, mempl.TxInfo{})
   383  	assert.NoError(t, err)
   384  
   385  	blockExec := sm.NewBlockExecutor(
   386  		stateStore,
   387  		logger,
   388  		proxyApp.Consensus(),
   389  		mempool,
   390  		sm.EmptyEvidencePool{},
   391  	)
   392  
   393  	commit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
   394  	block, _ := blockExec.CreateProposalBlock(
   395  		height,
   396  		state, commit,
   397  		proposerAddr,
   398  	)
   399  
   400  	pb, err := block.ToProto()
   401  	require.NoError(t, err)
   402  	// TODO(ismail): fix this test properly
   403  	// https://github.com/lazyledger/lazyledger-core/issues/77
   404  	assert.Less(t, int64(pb.Size()), maxBytes*2)
   405  
   406  	// check that the part set does not exceed the maximum block size
   407  	partSet := block.MakePartSet(partSize)
   408  	assert.EqualValues(t, partSet.ByteSize(), int64(pb.Size()))
   409  }
   410  
   411  func TestMaxProposalBlockSize(t *testing.T) {
   412  	config := cfg.ResetTestRoot("node_create_proposal")
   413  	defer os.RemoveAll(config.RootDir)
   414  	cc := proxy.NewLocalClientCreator(kvstore.NewApplication())
   415  	proxyApp := proxy.NewAppConns(cc)
   416  	err := proxyApp.Start()
   417  	require.Nil(t, err)
   418  	defer proxyApp.Stop() //nolint:errcheck // ignore for tests
   419  
   420  	logger := log.TestingLogger()
   421  
   422  	state, stateDB, _ := state(types.MaxVotesCount, int64(1))
   423  	stateStore := sm.NewStore(stateDB)
   424  	const maxBytes int64 = 1024 * 1024 * 2
   425  	state.ConsensusParams.Block.MaxBytes = maxBytes
   426  	proposerAddr, _ := state.Validators.GetByIndex(0)
   427  
   428  	// Make Mempool
   429  	mempool := mempl.NewCListMempool(
   430  		config.Mempool,
   431  		proxyApp.Mempool(),
   432  		state.LastBlockHeight,
   433  		mempl.WithMetrics(mempl.NopMetrics()),
   434  		mempl.WithPreCheck(sm.TxPreCheck(state)),
   435  		mempl.WithPostCheck(sm.TxPostCheck(state)),
   436  	)
   437  	mempool.SetLogger(logger)
   438  
   439  	// fill the mempool with one txs just below the maximum size
   440  	txLength := int(types.MaxDataBytesNoEvidence(maxBytes, types.MaxVotesCount))
   441  	tx := tmrand.Bytes(txLength - 6 - 4) // to account for the varint
   442  	err = mempool.CheckTx(tx, nil, mempl.TxInfo{})
   443  	assert.NoError(t, err)
   444  	// now produce more txs than what a normal block can hold with 10 smaller txs
   445  	// At the end of the test, only the single big tx should be added
   446  	for i := 0; i < 10; i++ {
   447  		tx := tmrand.Bytes(10)
   448  		err = mempool.CheckTx(tx, nil, mempl.TxInfo{})
   449  		assert.NoError(t, err)
   450  	}
   451  
   452  	blockExec := sm.NewBlockExecutor(
   453  		stateStore,
   454  		logger,
   455  		proxyApp.Consensus(),
   456  		mempool,
   457  		sm.EmptyEvidencePool{},
   458  	)
   459  
   460  	blockID := types.BlockID{
   461  		Hash: tmhash.Sum([]byte("blockID_hash")),
   462  		PartSetHeader: types.PartSetHeader{
   463  			Total: math.MaxInt32,
   464  			Hash:  tmhash.Sum([]byte("blockID_part_set_header_hash")),
   465  		},
   466  	}
   467  
   468  	timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC)
   469  	// change state in order to produce the largest accepted header
   470  	state.LastBlockID = blockID
   471  	state.LastBlockHeight = math.MaxInt64 - 1
   472  	state.LastBlockTime = timestamp
   473  	state.LastResultsHash = tmhash.Sum([]byte("last_results_hash"))
   474  	state.AppHash = tmhash.Sum([]byte("app_hash"))
   475  	state.Version.Consensus.Block = math.MaxInt64
   476  	state.Version.Consensus.App = math.MaxInt64
   477  	maxChainID := ""
   478  	for i := 0; i < types.MaxChainIDLen; i++ {
   479  		maxChainID += "𠜎"
   480  	}
   481  	state.ChainID = maxChainID
   482  
   483  	cs := types.CommitSig{
   484  		BlockIDFlag:      types.BlockIDFlagNil,
   485  		ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
   486  		Timestamp:        timestamp,
   487  		Signature:        crypto.CRandBytes(types.MaxSignatureSize),
   488  	}
   489  
   490  	commit := &types.Commit{
   491  		Height:  math.MaxInt64,
   492  		Round:   math.MaxInt32,
   493  		BlockID: blockID,
   494  	}
   495  
   496  	// add maximum amount of signatures to a single commit
   497  	for i := 0; i < types.MaxVotesCount; i++ {
   498  		commit.Signatures = append(commit.Signatures, cs)
   499  	}
   500  
   501  	block, partSet := blockExec.CreateProposalBlock(
   502  		math.MaxInt64,
   503  		state, commit,
   504  		proposerAddr,
   505  	)
   506  
   507  	// this ensures that the header is at max size
   508  	block.Header.Time = timestamp
   509  	block.Header.NumOriginalDataShares = math.MaxInt64
   510  
   511  	pb, err := block.ToProto()
   512  	require.NoError(t, err)
   513  
   514  	// require that the header and commit be the max possible size
   515  	require.Equal(t, int64(pb.Header.Size()), types.MaxHeaderBytes)
   516  	require.Equal(t, int64(pb.LastCommit.Size()), types.MaxCommitBytes(types.MaxVotesCount))
   517  	// make sure that the block is less than the max possible size
   518  	assert.LessOrEqual(t, maxBytes, int64(pb.Size()))
   519  	// because of the proto overhead we expect the part set bytes to be equal or
   520  	// less than the pb block size
   521  	assert.LessOrEqual(t, partSet.ByteSize(), int64(pb.Size()))
   522  
   523  }
   524  
   525  func TestNodeNewNodeCustomReactors(t *testing.T) {
   526  	config := cfg.ResetTestRoot("node_new_node_custom_reactors_test")
   527  	defer os.RemoveAll(config.RootDir)
   528  
   529  	cr := p2pmock.NewReactor()
   530  	customBlockchainReactor := p2pmock.NewReactor()
   531  
   532  	nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
   533  	require.NoError(t, err)
   534  	pval, err := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
   535  	require.NoError(t, err)
   536  
   537  	n, err := NewNode(config,
   538  		pval,
   539  		nodeKey,
   540  		proxy.DefaultClientCreator(config.ProxyApp, config.DBDir()),
   541  		DefaultGenesisDocProviderFunc(config),
   542  		InMemDBProvider,
   543  		ipfs.Mock(),
   544  		DefaultMetricsProvider(config.Instrumentation),
   545  		log.TestingLogger(),
   546  		CustomReactors(map[string]p2p.Reactor{"FOO": cr, "BLOCKCHAIN": customBlockchainReactor}),
   547  	)
   548  	require.NoError(t, err)
   549  
   550  	err = n.Start()
   551  	require.NoError(t, err)
   552  	defer n.Stop() //nolint:errcheck // ignore for tests
   553  
   554  	assert.True(t, cr.IsRunning())
   555  	assert.Equal(t, cr, n.Switch().Reactor("FOO"))
   556  
   557  	assert.True(t, customBlockchainReactor.IsRunning())
   558  	assert.Equal(t, customBlockchainReactor, n.Switch().Reactor("BLOCKCHAIN"))
   559  }
   560  
   561  func state(nVals int, height int64) (sm.State, dbm.DB, []types.PrivValidator) {
   562  	privVals := make([]types.PrivValidator, nVals)
   563  	vals := make([]types.GenesisValidator, nVals)
   564  	for i := 0; i < nVals; i++ {
   565  		privVal := types.NewMockPV()
   566  		privVals[i] = privVal
   567  		vals[i] = types.GenesisValidator{
   568  			Address: privVal.PrivKey.PubKey().Address(),
   569  			PubKey:  privVal.PrivKey.PubKey(),
   570  			Power:   1000,
   571  			Name:    fmt.Sprintf("test%d", i),
   572  		}
   573  	}
   574  	s, _ := sm.MakeGenesisState(&types.GenesisDoc{
   575  		ChainID:    "test-chain",
   576  		Validators: vals,
   577  		AppHash:    nil,
   578  	})
   579  
   580  	// save validators to db for 2 heights
   581  	stateDB := memdb.NewDB()
   582  	stateStore := sm.NewStore(stateDB)
   583  	if err := stateStore.Save(s); err != nil {
   584  		panic(err)
   585  	}
   586  
   587  	for i := 1; i < int(height); i++ {
   588  		s.LastBlockHeight++
   589  		s.LastValidators = s.Validators.Copy()
   590  		if err := stateStore.Save(s); err != nil {
   591  			panic(err)
   592  		}
   593  	}
   594  	return s, stateDB, privVals
   595  }