You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

590 lines
17 KiB

  1. package inspect_test
  2. import (
  3. "context"
  4. "fmt"
  5. "net"
  6. "os"
  7. "strings"
  8. "sync"
  9. "testing"
  10. "time"
  11. "github.com/fortytw2/leaktest"
  12. "github.com/stretchr/testify/mock"
  13. "github.com/stretchr/testify/require"
  14. abcitypes "github.com/tendermint/tendermint/abci/types"
  15. "github.com/tendermint/tendermint/config"
  16. "github.com/tendermint/tendermint/internal/inspect"
  17. "github.com/tendermint/tendermint/internal/state/indexer"
  18. indexermocks "github.com/tendermint/tendermint/internal/state/indexer/mocks"
  19. statemocks "github.com/tendermint/tendermint/internal/state/mocks"
  20. "github.com/tendermint/tendermint/libs/log"
  21. "github.com/tendermint/tendermint/libs/pubsub/query"
  22. "github.com/tendermint/tendermint/proto/tendermint/state"
  23. httpclient "github.com/tendermint/tendermint/rpc/client/http"
  24. "github.com/tendermint/tendermint/types"
  25. )
  26. func TestInspectConstructor(t *testing.T) {
  27. cfg, err := config.ResetTestRoot("test")
  28. require.NoError(t, err)
  29. testLogger := log.TestingLogger()
  30. t.Cleanup(leaktest.Check(t))
  31. defer func() { _ = os.RemoveAll(cfg.RootDir) }()
  32. t.Run("from config", func(t *testing.T) {
  33. logger := testLogger.With(t.Name())
  34. d, err := inspect.NewFromConfig(logger, cfg)
  35. require.NoError(t, err)
  36. require.NotNil(t, d)
  37. })
  38. }
  39. func TestInspectRun(t *testing.T) {
  40. cfg, err := config.ResetTestRoot("test")
  41. require.NoError(t, err)
  42. testLogger := log.TestingLogger()
  43. t.Cleanup(leaktest.Check(t))
  44. defer func() { _ = os.RemoveAll(cfg.RootDir) }()
  45. t.Run("from config", func(t *testing.T) {
  46. logger := testLogger.With(t.Name())
  47. d, err := inspect.NewFromConfig(logger, cfg)
  48. require.NoError(t, err)
  49. ctx, cancel := context.WithCancel(context.Background())
  50. stoppedWG := &sync.WaitGroup{}
  51. stoppedWG.Add(1)
  52. go func() {
  53. require.NoError(t, d.Run(ctx))
  54. stoppedWG.Done()
  55. }()
  56. cancel()
  57. stoppedWG.Wait()
  58. })
  59. }
  60. func TestBlock(t *testing.T) {
  61. testHeight := int64(1)
  62. testBlock := new(types.Block)
  63. testBlock.Header.Height = testHeight
  64. testBlock.Header.LastCommitHash = []byte("test hash")
  65. stateStoreMock := &statemocks.Store{}
  66. blockStoreMock := &statemocks.BlockStore{}
  67. blockStoreMock.On("Height").Return(testHeight)
  68. blockStoreMock.On("Base").Return(int64(0))
  69. blockStoreMock.On("LoadBlockMeta", testHeight).Return(&types.BlockMeta{})
  70. blockStoreMock.On("LoadBlock", testHeight).Return(testBlock)
  71. eventSinkMock := &indexermocks.EventSink{}
  72. eventSinkMock.On("Stop").Return(nil)
  73. rpcConfig := config.TestRPCConfig()
  74. l := log.TestingLogger()
  75. d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l)
  76. ctx, cancel := context.WithCancel(context.Background())
  77. wg := &sync.WaitGroup{}
  78. wg.Add(1)
  79. startedWG := &sync.WaitGroup{}
  80. startedWG.Add(1)
  81. go func() {
  82. startedWG.Done()
  83. defer wg.Done()
  84. require.NoError(t, d.Run(ctx))
  85. }()
  86. // FIXME: used to induce context switch.
  87. // Determine more deterministic method for prompting a context switch
  88. startedWG.Wait()
  89. requireConnect(t, rpcConfig.ListenAddress, 20)
  90. cli, err := httpclient.New(rpcConfig.ListenAddress)
  91. require.NoError(t, err)
  92. resultBlock, err := cli.Block(context.Background(), &testHeight)
  93. require.NoError(t, err)
  94. require.Equal(t, testBlock.Height, resultBlock.Block.Height)
  95. require.Equal(t, testBlock.LastCommitHash, resultBlock.Block.LastCommitHash)
  96. cancel()
  97. wg.Wait()
  98. blockStoreMock.AssertExpectations(t)
  99. stateStoreMock.AssertExpectations(t)
  100. }
  101. func TestTxSearch(t *testing.T) {
  102. testHash := []byte("test")
  103. testTx := []byte("tx")
  104. testQuery := fmt.Sprintf("tx.hash = '%s'", string(testHash))
  105. testTxResult := &abcitypes.TxResult{
  106. Height: 1,
  107. Index: 100,
  108. Tx: testTx,
  109. }
  110. stateStoreMock := &statemocks.Store{}
  111. blockStoreMock := &statemocks.BlockStore{}
  112. eventSinkMock := &indexermocks.EventSink{}
  113. eventSinkMock.On("Stop").Return(nil)
  114. eventSinkMock.On("Type").Return(indexer.KV)
  115. eventSinkMock.On("SearchTxEvents", mock.Anything,
  116. mock.MatchedBy(func(q *query.Query) bool { return testQuery == q.String() })).
  117. Return([]*abcitypes.TxResult{testTxResult}, nil)
  118. rpcConfig := config.TestRPCConfig()
  119. l := log.TestingLogger()
  120. d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l)
  121. ctx, cancel := context.WithCancel(context.Background())
  122. wg := &sync.WaitGroup{}
  123. wg.Add(1)
  124. startedWG := &sync.WaitGroup{}
  125. startedWG.Add(1)
  126. go func() {
  127. startedWG.Done()
  128. defer wg.Done()
  129. require.NoError(t, d.Run(ctx))
  130. }()
  131. // FIXME: used to induce context switch.
  132. // Determine more deterministic method for prompting a context switch
  133. startedWG.Wait()
  134. requireConnect(t, rpcConfig.ListenAddress, 20)
  135. cli, err := httpclient.New(rpcConfig.ListenAddress)
  136. require.NoError(t, err)
  137. var page = 1
  138. resultTxSearch, err := cli.TxSearch(context.Background(), testQuery, false, &page, &page, "")
  139. require.NoError(t, err)
  140. require.Len(t, resultTxSearch.Txs, 1)
  141. require.Equal(t, types.Tx(testTx), resultTxSearch.Txs[0].Tx)
  142. cancel()
  143. wg.Wait()
  144. eventSinkMock.AssertExpectations(t)
  145. stateStoreMock.AssertExpectations(t)
  146. blockStoreMock.AssertExpectations(t)
  147. }
  148. func TestTx(t *testing.T) {
  149. testHash := []byte("test")
  150. testTx := []byte("tx")
  151. stateStoreMock := &statemocks.Store{}
  152. blockStoreMock := &statemocks.BlockStore{}
  153. eventSinkMock := &indexermocks.EventSink{}
  154. eventSinkMock.On("Stop").Return(nil)
  155. eventSinkMock.On("Type").Return(indexer.KV)
  156. eventSinkMock.On("GetTxByHash", testHash).Return(&abcitypes.TxResult{
  157. Tx: testTx,
  158. }, nil)
  159. rpcConfig := config.TestRPCConfig()
  160. l := log.TestingLogger()
  161. d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l)
  162. ctx, cancel := context.WithCancel(context.Background())
  163. wg := &sync.WaitGroup{}
  164. wg.Add(1)
  165. startedWG := &sync.WaitGroup{}
  166. startedWG.Add(1)
  167. go func() {
  168. startedWG.Done()
  169. defer wg.Done()
  170. require.NoError(t, d.Run(ctx))
  171. }()
  172. // FIXME: used to induce context switch.
  173. // Determine more deterministic method for prompting a context switch
  174. startedWG.Wait()
  175. requireConnect(t, rpcConfig.ListenAddress, 20)
  176. cli, err := httpclient.New(rpcConfig.ListenAddress)
  177. require.NoError(t, err)
  178. res, err := cli.Tx(context.Background(), testHash, false)
  179. require.NoError(t, err)
  180. require.Equal(t, types.Tx(testTx), res.Tx)
  181. cancel()
  182. wg.Wait()
  183. eventSinkMock.AssertExpectations(t)
  184. stateStoreMock.AssertExpectations(t)
  185. blockStoreMock.AssertExpectations(t)
  186. }
  187. func TestConsensusParams(t *testing.T) {
  188. testHeight := int64(1)
  189. testMaxGas := int64(55)
  190. stateStoreMock := &statemocks.Store{}
  191. blockStoreMock := &statemocks.BlockStore{}
  192. blockStoreMock.On("Height").Return(testHeight)
  193. blockStoreMock.On("Base").Return(int64(0))
  194. stateStoreMock.On("LoadConsensusParams", testHeight).Return(types.ConsensusParams{
  195. Block: types.BlockParams{
  196. MaxGas: testMaxGas,
  197. },
  198. }, nil)
  199. eventSinkMock := &indexermocks.EventSink{}
  200. eventSinkMock.On("Stop").Return(nil)
  201. rpcConfig := config.TestRPCConfig()
  202. l := log.TestingLogger()
  203. d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l)
  204. ctx, cancel := context.WithCancel(context.Background())
  205. wg := &sync.WaitGroup{}
  206. wg.Add(1)
  207. startedWG := &sync.WaitGroup{}
  208. startedWG.Add(1)
  209. go func() {
  210. startedWG.Done()
  211. defer wg.Done()
  212. require.NoError(t, d.Run(ctx))
  213. }()
  214. // FIXME: used to induce context switch.
  215. // Determine more deterministic method for prompting a context switch
  216. startedWG.Wait()
  217. requireConnect(t, rpcConfig.ListenAddress, 20)
  218. cli, err := httpclient.New(rpcConfig.ListenAddress)
  219. require.NoError(t, err)
  220. params, err := cli.ConsensusParams(context.Background(), &testHeight)
  221. require.NoError(t, err)
  222. require.Equal(t, params.ConsensusParams.Block.MaxGas, testMaxGas)
  223. cancel()
  224. wg.Wait()
  225. blockStoreMock.AssertExpectations(t)
  226. stateStoreMock.AssertExpectations(t)
  227. }
  228. func TestBlockResults(t *testing.T) {
  229. testHeight := int64(1)
  230. testGasUsed := int64(100)
  231. stateStoreMock := &statemocks.Store{}
  232. // tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
  233. stateStoreMock.On("LoadABCIResponses", testHeight).Return(&state.ABCIResponses{
  234. DeliverTxs: []*abcitypes.ResponseDeliverTx{
  235. {
  236. GasUsed: testGasUsed,
  237. },
  238. },
  239. EndBlock: &abcitypes.ResponseEndBlock{},
  240. BeginBlock: &abcitypes.ResponseBeginBlock{},
  241. }, nil)
  242. blockStoreMock := &statemocks.BlockStore{}
  243. blockStoreMock.On("Base").Return(int64(0))
  244. blockStoreMock.On("Height").Return(testHeight)
  245. eventSinkMock := &indexermocks.EventSink{}
  246. eventSinkMock.On("Stop").Return(nil)
  247. rpcConfig := config.TestRPCConfig()
  248. l := log.TestingLogger()
  249. d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l)
  250. ctx, cancel := context.WithCancel(context.Background())
  251. wg := &sync.WaitGroup{}
  252. wg.Add(1)
  253. startedWG := &sync.WaitGroup{}
  254. startedWG.Add(1)
  255. go func() {
  256. startedWG.Done()
  257. defer wg.Done()
  258. require.NoError(t, d.Run(ctx))
  259. }()
  260. // FIXME: used to induce context switch.
  261. // Determine more deterministic method for prompting a context switch
  262. startedWG.Wait()
  263. requireConnect(t, rpcConfig.ListenAddress, 20)
  264. cli, err := httpclient.New(rpcConfig.ListenAddress)
  265. require.NoError(t, err)
  266. res, err := cli.BlockResults(context.Background(), &testHeight)
  267. require.NoError(t, err)
  268. require.Equal(t, res.TotalGasUsed, testGasUsed)
  269. cancel()
  270. wg.Wait()
  271. blockStoreMock.AssertExpectations(t)
  272. stateStoreMock.AssertExpectations(t)
  273. }
  274. func TestCommit(t *testing.T) {
  275. testHeight := int64(1)
  276. testRound := int32(101)
  277. stateStoreMock := &statemocks.Store{}
  278. blockStoreMock := &statemocks.BlockStore{}
  279. blockStoreMock.On("Base").Return(int64(0))
  280. blockStoreMock.On("Height").Return(testHeight)
  281. blockStoreMock.On("LoadBlockMeta", testHeight).Return(&types.BlockMeta{}, nil)
  282. blockStoreMock.On("LoadSeenCommit").Return(&types.Commit{
  283. Height: testHeight,
  284. Round: testRound,
  285. }, nil)
  286. eventSinkMock := &indexermocks.EventSink{}
  287. eventSinkMock.On("Stop").Return(nil)
  288. rpcConfig := config.TestRPCConfig()
  289. l := log.TestingLogger()
  290. d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l)
  291. ctx, cancel := context.WithCancel(context.Background())
  292. wg := &sync.WaitGroup{}
  293. wg.Add(1)
  294. startedWG := &sync.WaitGroup{}
  295. startedWG.Add(1)
  296. go func() {
  297. startedWG.Done()
  298. defer wg.Done()
  299. require.NoError(t, d.Run(ctx))
  300. }()
  301. // FIXME: used to induce context switch.
  302. // Determine more deterministic method for prompting a context switch
  303. startedWG.Wait()
  304. requireConnect(t, rpcConfig.ListenAddress, 20)
  305. cli, err := httpclient.New(rpcConfig.ListenAddress)
  306. require.NoError(t, err)
  307. res, err := cli.Commit(context.Background(), &testHeight)
  308. require.NoError(t, err)
  309. require.NotNil(t, res)
  310. require.Equal(t, res.SignedHeader.Commit.Round, testRound)
  311. cancel()
  312. wg.Wait()
  313. blockStoreMock.AssertExpectations(t)
  314. stateStoreMock.AssertExpectations(t)
  315. }
  316. func TestBlockByHash(t *testing.T) {
  317. testHeight := int64(1)
  318. testHash := []byte("test hash")
  319. testBlock := new(types.Block)
  320. testBlock.Header.Height = testHeight
  321. testBlock.Header.LastCommitHash = testHash
  322. stateStoreMock := &statemocks.Store{}
  323. blockStoreMock := &statemocks.BlockStore{}
  324. blockStoreMock.On("LoadBlockMeta", testHeight).Return(&types.BlockMeta{
  325. BlockID: types.BlockID{
  326. Hash: testHash,
  327. },
  328. Header: types.Header{
  329. Height: testHeight,
  330. },
  331. }, nil)
  332. blockStoreMock.On("LoadBlockByHash", testHash).Return(testBlock, nil)
  333. eventSinkMock := &indexermocks.EventSink{}
  334. eventSinkMock.On("Stop").Return(nil)
  335. rpcConfig := config.TestRPCConfig()
  336. l := log.TestingLogger()
  337. d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l)
  338. ctx, cancel := context.WithCancel(context.Background())
  339. wg := &sync.WaitGroup{}
  340. wg.Add(1)
  341. startedWG := &sync.WaitGroup{}
  342. startedWG.Add(1)
  343. go func() {
  344. startedWG.Done()
  345. defer wg.Done()
  346. require.NoError(t, d.Run(ctx))
  347. }()
  348. // FIXME: used to induce context switch.
  349. // Determine more deterministic method for prompting a context switch
  350. startedWG.Wait()
  351. requireConnect(t, rpcConfig.ListenAddress, 20)
  352. cli, err := httpclient.New(rpcConfig.ListenAddress)
  353. require.NoError(t, err)
  354. res, err := cli.BlockByHash(context.Background(), testHash)
  355. require.NoError(t, err)
  356. require.NotNil(t, res)
  357. require.Equal(t, []byte(res.BlockID.Hash), testHash)
  358. cancel()
  359. wg.Wait()
  360. blockStoreMock.AssertExpectations(t)
  361. stateStoreMock.AssertExpectations(t)
  362. }
  363. func TestBlockchain(t *testing.T) {
  364. testHeight := int64(1)
  365. testBlock := new(types.Block)
  366. testBlockHash := []byte("test hash")
  367. testBlock.Header.Height = testHeight
  368. testBlock.Header.LastCommitHash = testBlockHash
  369. stateStoreMock := &statemocks.Store{}
  370. blockStoreMock := &statemocks.BlockStore{}
  371. blockStoreMock.On("Height").Return(testHeight)
  372. blockStoreMock.On("Base").Return(int64(0))
  373. blockStoreMock.On("LoadBlockMeta", testHeight).Return(&types.BlockMeta{
  374. BlockID: types.BlockID{
  375. Hash: testBlockHash,
  376. },
  377. })
  378. eventSinkMock := &indexermocks.EventSink{}
  379. eventSinkMock.On("Stop").Return(nil)
  380. rpcConfig := config.TestRPCConfig()
  381. l := log.TestingLogger()
  382. d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l)
  383. ctx, cancel := context.WithCancel(context.Background())
  384. wg := &sync.WaitGroup{}
  385. wg.Add(1)
  386. startedWG := &sync.WaitGroup{}
  387. startedWG.Add(1)
  388. go func() {
  389. startedWG.Done()
  390. defer wg.Done()
  391. require.NoError(t, d.Run(ctx))
  392. }()
  393. // FIXME: used to induce context switch.
  394. // Determine more deterministic method for prompting a context switch
  395. startedWG.Wait()
  396. requireConnect(t, rpcConfig.ListenAddress, 20)
  397. cli, err := httpclient.New(rpcConfig.ListenAddress)
  398. require.NoError(t, err)
  399. res, err := cli.BlockchainInfo(context.Background(), 0, 100)
  400. require.NoError(t, err)
  401. require.NotNil(t, res)
  402. require.Equal(t, testBlockHash, []byte(res.BlockMetas[0].BlockID.Hash))
  403. cancel()
  404. wg.Wait()
  405. blockStoreMock.AssertExpectations(t)
  406. stateStoreMock.AssertExpectations(t)
  407. }
  408. func TestValidators(t *testing.T) {
  409. testHeight := int64(1)
  410. testVotingPower := int64(100)
  411. testValidators := types.ValidatorSet{
  412. Validators: []*types.Validator{
  413. {
  414. VotingPower: testVotingPower,
  415. },
  416. },
  417. }
  418. stateStoreMock := &statemocks.Store{}
  419. stateStoreMock.On("LoadValidators", testHeight).Return(&testValidators, nil)
  420. blockStoreMock := &statemocks.BlockStore{}
  421. blockStoreMock.On("Height").Return(testHeight)
  422. blockStoreMock.On("Base").Return(int64(0))
  423. eventSinkMock := &indexermocks.EventSink{}
  424. eventSinkMock.On("Stop").Return(nil)
  425. rpcConfig := config.TestRPCConfig()
  426. l := log.TestingLogger()
  427. d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l)
  428. ctx, cancel := context.WithCancel(context.Background())
  429. wg := &sync.WaitGroup{}
  430. wg.Add(1)
  431. startedWG := &sync.WaitGroup{}
  432. startedWG.Add(1)
  433. go func() {
  434. startedWG.Done()
  435. defer wg.Done()
  436. require.NoError(t, d.Run(ctx))
  437. }()
  438. // FIXME: used to induce context switch.
  439. // Determine more deterministic method for prompting a context switch
  440. startedWG.Wait()
  441. requireConnect(t, rpcConfig.ListenAddress, 20)
  442. cli, err := httpclient.New(rpcConfig.ListenAddress)
  443. require.NoError(t, err)
  444. testPage := 1
  445. testPerPage := 100
  446. res, err := cli.Validators(context.Background(), &testHeight, &testPage, &testPerPage)
  447. require.NoError(t, err)
  448. require.NotNil(t, res)
  449. require.Equal(t, testVotingPower, res.Validators[0].VotingPower)
  450. cancel()
  451. wg.Wait()
  452. blockStoreMock.AssertExpectations(t)
  453. stateStoreMock.AssertExpectations(t)
  454. }
  455. func TestBlockSearch(t *testing.T) {
  456. testHeight := int64(1)
  457. testBlockHash := []byte("test hash")
  458. testQuery := "block.height = 1"
  459. stateStoreMock := &statemocks.Store{}
  460. blockStoreMock := &statemocks.BlockStore{}
  461. eventSinkMock := &indexermocks.EventSink{}
  462. eventSinkMock.On("Stop").Return(nil)
  463. eventSinkMock.On("Type").Return(indexer.KV)
  464. blockStoreMock.On("LoadBlock", testHeight).Return(&types.Block{
  465. Header: types.Header{
  466. Height: testHeight,
  467. },
  468. }, nil)
  469. blockStoreMock.On("LoadBlockMeta", testHeight).Return(&types.BlockMeta{
  470. BlockID: types.BlockID{
  471. Hash: testBlockHash,
  472. },
  473. })
  474. eventSinkMock.On("SearchBlockEvents", mock.Anything,
  475. mock.MatchedBy(func(q *query.Query) bool { return testQuery == q.String() })).
  476. Return([]int64{testHeight}, nil)
  477. rpcConfig := config.TestRPCConfig()
  478. l := log.TestingLogger()
  479. d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l)
  480. ctx, cancel := context.WithCancel(context.Background())
  481. wg := &sync.WaitGroup{}
  482. wg.Add(1)
  483. startedWG := &sync.WaitGroup{}
  484. startedWG.Add(1)
  485. go func() {
  486. startedWG.Done()
  487. defer wg.Done()
  488. require.NoError(t, d.Run(ctx))
  489. }()
  490. // FIXME: used to induce context switch.
  491. // Determine more deterministic method for prompting a context switch
  492. startedWG.Wait()
  493. requireConnect(t, rpcConfig.ListenAddress, 20)
  494. cli, err := httpclient.New(rpcConfig.ListenAddress)
  495. require.NoError(t, err)
  496. testPage := 1
  497. testPerPage := 100
  498. testOrderBy := "desc"
  499. res, err := cli.BlockSearch(context.Background(), testQuery, &testPage, &testPerPage, testOrderBy)
  500. require.NoError(t, err)
  501. require.NotNil(t, res)
  502. require.Equal(t, testBlockHash, []byte(res.Blocks[0].BlockID.Hash))
  503. cancel()
  504. wg.Wait()
  505. blockStoreMock.AssertExpectations(t)
  506. stateStoreMock.AssertExpectations(t)
  507. }
  508. func requireConnect(t testing.TB, addr string, retries int) {
  509. parts := strings.SplitN(addr, "://", 2)
  510. if len(parts) != 2 {
  511. t.Fatalf("malformed address to dial: %s", addr)
  512. }
  513. var err error
  514. for i := 0; i < retries; i++ {
  515. var conn net.Conn
  516. conn, err = net.Dial(parts[0], parts[1])
  517. if err == nil {
  518. conn.Close()
  519. return
  520. }
  521. // FIXME attempt to yield and let the other goroutine continue execution.
  522. time.Sleep(time.Microsecond * 100)
  523. }
  524. t.Fatalf("unable to connect to server %s after %d tries: %s", addr, retries, err)
  525. }