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.

259 lines
6.9 KiB

  1. package light_test
  2. import (
  3. "context"
  4. "os"
  5. "testing"
  6. "time"
  7. "github.com/stretchr/testify/require"
  8. dbm "github.com/tendermint/tm-db"
  9. "github.com/tendermint/tendermint/abci/example/kvstore"
  10. "github.com/tendermint/tendermint/libs/log"
  11. "github.com/tendermint/tendermint/light"
  12. "github.com/tendermint/tendermint/light/provider"
  13. httpp "github.com/tendermint/tendermint/light/provider/http"
  14. dbs "github.com/tendermint/tendermint/light/store/db"
  15. rpctest "github.com/tendermint/tendermint/rpc/test"
  16. "github.com/tendermint/tendermint/types"
  17. )
  18. // NOTE: these are ports of the tests from example_test.go but
  19. // rewritten as more conventional tests.
  20. // Automatically getting new headers and verifying them.
  21. func TestClientIntegration_Update(t *testing.T) {
  22. t.Parallel()
  23. ctx, cancel := context.WithCancel(context.Background())
  24. defer cancel()
  25. conf, err := rpctest.CreateConfig(t.Name())
  26. require.NoError(t, err)
  27. logger := log.NewTestingLogger(t)
  28. // Start a test application
  29. app := kvstore.NewApplication()
  30. _, closer, err := rpctest.StartTendermint(ctx, conf, app, rpctest.SuppressStdout)
  31. require.NoError(t, err)
  32. defer func() { require.NoError(t, closer(ctx)) }()
  33. // give Tendermint time to generate some blocks
  34. time.Sleep(5 * time.Second)
  35. dbDir, err := os.MkdirTemp("", "light-client-test-update-example")
  36. require.NoError(t, err)
  37. defer os.RemoveAll(dbDir)
  38. chainID := conf.ChainID()
  39. primary, err := httpp.New(chainID, conf.RPC.ListenAddress)
  40. require.NoError(t, err)
  41. // give Tendermint time to generate some blocks
  42. block, err := waitForBlock(ctx, primary, 2)
  43. require.NoError(t, err)
  44. db, err := dbm.NewGoLevelDB("light-client-db", dbDir)
  45. require.NoError(t, err)
  46. c, err := light.NewClient(
  47. ctx,
  48. chainID,
  49. light.TrustOptions{
  50. Period: 504 * time.Hour, // 21 days
  51. Height: 2,
  52. Hash: block.Hash(),
  53. },
  54. primary,
  55. []provider.Provider{primary}, // NOTE: primary should not be used here
  56. dbs.New(db),
  57. light.Logger(logger),
  58. )
  59. require.NoError(t, err)
  60. defer func() { require.NoError(t, c.Cleanup()) }()
  61. // ensure Tendermint is at height 3 or higher
  62. _, err = waitForBlock(ctx, primary, 3)
  63. require.NoError(t, err)
  64. h, err := c.Update(ctx, time.Now())
  65. require.NoError(t, err)
  66. require.NotNil(t, h)
  67. require.True(t, h.Height > 2)
  68. }
  69. // Manually getting light blocks and verifying them.
  70. func TestClientIntegration_VerifyLightBlockAtHeight(t *testing.T) {
  71. t.Parallel()
  72. ctx, cancel := context.WithCancel(context.Background())
  73. defer cancel()
  74. conf, err := rpctest.CreateConfig(t.Name())
  75. require.NoError(t, err)
  76. logger := log.NewTestingLogger(t)
  77. // Start a test application
  78. app := kvstore.NewApplication()
  79. _, closer, err := rpctest.StartTendermint(ctx, conf, app, rpctest.SuppressStdout)
  80. require.NoError(t, err)
  81. defer func() { require.NoError(t, closer(ctx)) }()
  82. dbDir, err := os.MkdirTemp("", "light-client-test-verify-example")
  83. require.NoError(t, err)
  84. defer os.RemoveAll(dbDir)
  85. chainID := conf.ChainID()
  86. primary, err := httpp.New(chainID, conf.RPC.ListenAddress)
  87. require.NoError(t, err)
  88. // give Tendermint time to generate some blocks
  89. block, err := waitForBlock(ctx, primary, 2)
  90. require.NoError(t, err)
  91. db, err := dbm.NewGoLevelDB("light-client-db", dbDir)
  92. require.NoError(t, err)
  93. c, err := light.NewClient(ctx,
  94. chainID,
  95. light.TrustOptions{
  96. Period: 504 * time.Hour, // 21 days
  97. Height: 2,
  98. Hash: block.Hash(),
  99. },
  100. primary,
  101. []provider.Provider{primary}, // NOTE: primary should not be used here
  102. dbs.New(db),
  103. light.Logger(logger),
  104. )
  105. require.NoError(t, err)
  106. defer func() { require.NoError(t, c.Cleanup()) }()
  107. // ensure Tendermint is at height 3 or higher
  108. _, err = waitForBlock(ctx, primary, 3)
  109. require.NoError(t, err)
  110. _, err = c.VerifyLightBlockAtHeight(ctx, 3, time.Now())
  111. require.NoError(t, err)
  112. h, err := c.TrustedLightBlock(3)
  113. require.NoError(t, err)
  114. require.EqualValues(t, 3, h.Height)
  115. }
  116. func waitForBlock(ctx context.Context, p provider.Provider, height int64) (*types.LightBlock, error) {
  117. for {
  118. block, err := p.LightBlock(ctx, height)
  119. switch err {
  120. case nil:
  121. return block, nil
  122. // node isn't running yet, wait 1 second and repeat
  123. case provider.ErrNoResponse, provider.ErrHeightTooHigh:
  124. timer := time.NewTimer(1 * time.Second)
  125. select {
  126. case <-ctx.Done():
  127. return nil, ctx.Err()
  128. case <-timer.C:
  129. }
  130. default:
  131. return nil, err
  132. }
  133. }
  134. }
  135. func TestClientStatusRPC(t *testing.T) {
  136. ctx, cancel := context.WithCancel(context.Background())
  137. defer cancel()
  138. conf, err := rpctest.CreateConfig(t.Name())
  139. require.NoError(t, err)
  140. // Start a test application
  141. app := kvstore.NewApplication()
  142. _, closer, err := rpctest.StartTendermint(ctx, conf, app, rpctest.SuppressStdout)
  143. require.NoError(t, err)
  144. defer func() { require.NoError(t, closer(ctx)) }()
  145. dbDir, err := os.MkdirTemp("", "light-client-test-status-example")
  146. require.NoError(t, err)
  147. t.Cleanup(func() { os.RemoveAll(dbDir) })
  148. chainID := conf.ChainID()
  149. primary, err := httpp.New(chainID, conf.RPC.ListenAddress)
  150. require.NoError(t, err)
  151. // give Tendermint time to generate some blocks
  152. block, err := waitForBlock(ctx, primary, 2)
  153. require.NoError(t, err)
  154. db, err := dbm.NewGoLevelDB("light-client-db", dbDir)
  155. require.NoError(t, err)
  156. // In order to not create a full testnet to verify whether we get the correct IPs
  157. // if we have more than one witness, we add the primary multiple times
  158. // TODO This should be buggy behavior, we should not be allowed to add the same nodes as witnesses
  159. witnesses := []provider.Provider{primary, primary, primary}
  160. c, err := light.NewClient(ctx,
  161. chainID,
  162. light.TrustOptions{
  163. Period: 504 * time.Hour, // 21 days
  164. Height: 2,
  165. Hash: block.Hash(),
  166. },
  167. primary,
  168. witnesses,
  169. dbs.New(db),
  170. light.Logger(log.TestingLogger()),
  171. )
  172. require.NoError(t, err)
  173. defer func() { require.NoError(t, c.Cleanup()) }()
  174. lightStatus := c.Status(ctx)
  175. // Verify primary IP
  176. require.True(t, lightStatus.PrimaryID == primary.ID())
  177. // Verify IPs of witnesses
  178. require.ElementsMatch(t, mapProviderArrayToIP(witnesses), lightStatus.WitnessesID)
  179. // Verify that number of peers is equal to number of witnesses (+ 1 if the primary is not a witness)
  180. require.Equal(t, len(witnesses)+1*primaryNotInWitnessList(witnesses, primary), lightStatus.NumPeers)
  181. // Verify that the last trusted hash returned matches the stored hash of the trusted
  182. // block at the last trusted height.
  183. blockAtTrustedHeight, err := c.TrustedLightBlock(lightStatus.LastTrustedHeight)
  184. require.NoError(t, err)
  185. require.EqualValues(t, lightStatus.LastTrustedHash, blockAtTrustedHeight.Hash())
  186. }
  187. // Extract the IP address of all the providers within an array
  188. func mapProviderArrayToIP(el []provider.Provider) []string {
  189. ips := make([]string, len(el))
  190. for i, v := range el {
  191. ips[i] = v.ID()
  192. }
  193. return ips
  194. }
  195. // If the primary is not in the witness list, we will return 1
  196. // Otherwise, return 0
  197. func primaryNotInWitnessList(witnesses []provider.Provider, primary provider.Provider) int {
  198. for _, el := range witnesses {
  199. if el == primary {
  200. return 0
  201. }
  202. }
  203. return 1
  204. }