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.

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