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.

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