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.

180 lines
4.2 KiB

  1. package statesync
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "sync"
  7. "testing"
  8. "time"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/stretchr/testify/require"
  11. "github.com/tendermint/tendermint/internal/p2p"
  12. ssproto "github.com/tendermint/tendermint/proto/tendermint/statesync"
  13. "github.com/tendermint/tendermint/types"
  14. )
  15. func TestDispatcherBasic(t *testing.T) {
  16. ch := make(chan p2p.Envelope, 100)
  17. closeCh := make(chan struct{})
  18. defer close(closeCh)
  19. d := newDispatcher(ch, 1*time.Second)
  20. go handleRequests(t, d, ch, closeCh)
  21. peers := createPeerSet(5)
  22. for _, peer := range peers {
  23. d.addPeer(peer)
  24. }
  25. wg := sync.WaitGroup{}
  26. // make a bunch of async requests and require that the correct responses are
  27. // given
  28. for i := 1; i < 10; i++ {
  29. wg.Add(1)
  30. go func(height int64) {
  31. defer wg.Done()
  32. lb, peer, err := d.LightBlock(context.Background(), height)
  33. require.NoError(t, err)
  34. require.NotNil(t, lb)
  35. require.Equal(t, lb.Height, height)
  36. require.Contains(t, peers, peer)
  37. }(int64(i))
  38. }
  39. wg.Wait()
  40. }
  41. func TestDispatcherProviders(t *testing.T) {
  42. ch := make(chan p2p.Envelope, 100)
  43. chainID := "state-sync-test"
  44. closeCh := make(chan struct{})
  45. defer close(closeCh)
  46. d := newDispatcher(ch, 1*time.Second)
  47. go handleRequests(t, d, ch, closeCh)
  48. peers := createPeerSet(5)
  49. for _, peer := range peers {
  50. d.addPeer(peer)
  51. }
  52. providers := d.Providers(chainID, 5*time.Second)
  53. require.Len(t, providers, 5)
  54. for i, p := range providers {
  55. bp, ok := p.(*blockProvider)
  56. require.True(t, ok)
  57. assert.Equal(t, bp.String(), string(peers[i]))
  58. lb, err := p.LightBlock(context.Background(), 10)
  59. assert.Error(t, err)
  60. assert.Nil(t, lb)
  61. }
  62. }
  63. func TestPeerListBasic(t *testing.T) {
  64. peerList := newPeerList()
  65. assert.Zero(t, peerList.Len())
  66. numPeers := 10
  67. peerSet := createPeerSet(numPeers)
  68. for _, peer := range peerSet {
  69. peerList.Append(peer)
  70. }
  71. for idx, peer := range peerList.Peers() {
  72. assert.Equal(t, peer, peerSet[idx])
  73. }
  74. assert.Equal(t, numPeers, peerList.Len())
  75. half := numPeers / 2
  76. for i := 0; i < half; i++ {
  77. assert.Equal(t, peerSet[i], peerList.Pop())
  78. }
  79. assert.Equal(t, half, peerList.Len())
  80. peerList.Remove(types.NodeID("lp"))
  81. assert.Equal(t, half, peerList.Len())
  82. peerList.Remove(peerSet[half])
  83. half++
  84. assert.Equal(t, peerSet[half], peerList.Pop())
  85. }
  86. func TestPeerListConcurrent(t *testing.T) {
  87. peerList := newPeerList()
  88. numPeers := 10
  89. wg := sync.WaitGroup{}
  90. // we run a set of goroutines requesting the next peer in the list. As the
  91. // peer list hasn't been populated each these go routines should block
  92. for i := 0; i < numPeers/2; i++ {
  93. go func() {
  94. _ = peerList.Pop()
  95. wg.Done()
  96. }()
  97. }
  98. // now we add the peers to the list, this should allow the previously
  99. // blocked go routines to unblock
  100. for _, peer := range createPeerSet(numPeers) {
  101. wg.Add(1)
  102. peerList.Append(peer)
  103. }
  104. // we request the second half of the peer set
  105. for i := 0; i < numPeers/2; i++ {
  106. go func() {
  107. _ = peerList.Pop()
  108. wg.Done()
  109. }()
  110. }
  111. // we use a context with cancel and a separate go routine to wait for all
  112. // the other goroutines to close.
  113. ctx, cancel := context.WithCancel(context.Background())
  114. go func() { wg.Wait(); cancel() }()
  115. select {
  116. case <-time.After(time.Second):
  117. // not all of the blocked go routines waiting on peers have closed after
  118. // one second. This likely means the list got blocked.
  119. t.Failed()
  120. case <-ctx.Done():
  121. // there should be no peers remaining
  122. require.Equal(t, 0, peerList.Len())
  123. }
  124. }
  125. // handleRequests is a helper function usually run in a separate go routine to
  126. // imitate the expected responses of the reactor wired to the dispatcher
  127. func handleRequests(t *testing.T, d *dispatcher, ch chan p2p.Envelope, closeCh chan struct{}) {
  128. t.Helper()
  129. for {
  130. select {
  131. case request := <-ch:
  132. height := request.Message.(*ssproto.LightBlockRequest).Height
  133. peer := request.To
  134. resp := mockLBResp(t, peer, int64(height), time.Now())
  135. block, _ := resp.block.ToProto()
  136. require.NoError(t, d.respond(block, resp.peer))
  137. case <-closeCh:
  138. return
  139. }
  140. }
  141. }
  142. func createPeerSet(num int) []types.NodeID {
  143. peers := make([]types.NodeID, num)
  144. for i := 0; i < num; i++ {
  145. peers[i], _ = types.NewNodeID(strings.Repeat(fmt.Sprintf("%d", i), 2*types.NodeIDByteLength))
  146. }
  147. return peers
  148. }