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 }