github.com/status-im/status-go@v1.1.0/mailserver/mailserver_test.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package mailserver
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"encoding/binary"
    22  	"errors"
    23  	"fmt"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/stretchr/testify/suite"
    28  
    29  	"github.com/ethereum/go-ethereum/common"
    30  	"github.com/ethereum/go-ethereum/crypto"
    31  	"github.com/ethereum/go-ethereum/rlp"
    32  
    33  	"github.com/status-im/status-go/eth-node/types"
    34  	"github.com/status-im/status-go/params"
    35  	waku "github.com/status-im/status-go/waku"
    36  	wakucommon "github.com/status-im/status-go/waku/common"
    37  )
    38  
    39  const powRequirement = 0.00001
    40  
    41  var keyID string
    42  var seed = time.Now().Unix()
    43  var testPayload = []byte("test payload")
    44  
    45  type ServerTestParams struct {
    46  	topic types.TopicType
    47  	birth uint32
    48  	low   uint32
    49  	upp   uint32
    50  	limit uint32
    51  	key   *ecdsa.PrivateKey
    52  }
    53  
    54  func TestMailserverSuite(t *testing.T) {
    55  	suite.Run(t, new(MailserverSuite))
    56  }
    57  
    58  type MailserverSuite struct {
    59  	suite.Suite
    60  	server  *WakuMailServer
    61  	shh     *waku.Waku
    62  	config  *params.WakuConfig
    63  	dataDir string
    64  }
    65  
    66  func (s *MailserverSuite) SetupTest() {
    67  	s.server = &WakuMailServer{}
    68  	s.shh = waku.New(&waku.DefaultConfig, nil)
    69  	s.shh.RegisterMailServer(s.server)
    70  
    71  	tmpDir := s.T().TempDir()
    72  	s.dataDir = tmpDir
    73  
    74  	s.config = &params.WakuConfig{
    75  		DataDir:            tmpDir,
    76  		MailServerPassword: "testpassword",
    77  	}
    78  }
    79  
    80  func (s *MailserverSuite) TestInit() {
    81  	testCases := []struct {
    82  		config        params.WakuConfig
    83  		expectedError error
    84  		info          string
    85  	}{
    86  		{
    87  			config:        params.WakuConfig{DataDir: ""},
    88  			expectedError: errDirectoryNotProvided,
    89  			info:          "config with empty DataDir",
    90  		},
    91  		{
    92  			config: params.WakuConfig{
    93  				DataDir:            s.config.DataDir,
    94  				MailServerPassword: "pwd",
    95  			},
    96  			expectedError: nil,
    97  			info:          "config with correct DataDir and Password",
    98  		},
    99  		{
   100  			config: params.WakuConfig{
   101  				DataDir:             s.config.DataDir,
   102  				MailServerPassword:  "pwd",
   103  				MailServerRateLimit: 5,
   104  			},
   105  			expectedError: nil,
   106  			info:          "config with rate limit",
   107  		},
   108  	}
   109  
   110  	for _, testCase := range testCases {
   111  		// to satisfy gosec: C601 checks
   112  		tc := testCase
   113  		s.T().Run(tc.info, func(*testing.T) {
   114  			mailServer := &WakuMailServer{}
   115  			shh := waku.New(&waku.DefaultConfig, nil)
   116  			shh.RegisterMailServer(mailServer)
   117  
   118  			err := mailServer.Init(shh, &tc.config)
   119  			s.Require().Equal(tc.expectedError, err)
   120  			if err == nil {
   121  				defer mailServer.Close()
   122  			}
   123  
   124  			// db should be open only if there was no error
   125  			if tc.expectedError == nil {
   126  				s.NotNil(mailServer.ms.db)
   127  			} else {
   128  				s.Nil(mailServer.ms)
   129  			}
   130  
   131  			if tc.config.MailServerRateLimit > 0 {
   132  				s.NotNil(mailServer.ms.rateLimiter)
   133  			}
   134  		})
   135  	}
   136  }
   137  
   138  func (s *MailserverSuite) TestArchive() {
   139  	config := *s.config
   140  
   141  	err := s.server.Init(s.shh, &config)
   142  	s.Require().NoError(err)
   143  	defer s.server.Close()
   144  
   145  	env, err := generateEnvelope(time.Now())
   146  	s.NoError(err)
   147  	rawEnvelope, err := rlp.EncodeToBytes(env)
   148  	s.NoError(err)
   149  
   150  	s.server.Archive(env)
   151  	key := NewDBKey(env.Expiry-env.TTL, types.TopicType(env.Topic), types.Hash(env.Hash()))
   152  	archivedEnvelope, err := s.server.ms.db.GetEnvelope(key)
   153  	s.NoError(err)
   154  
   155  	s.Equal(rawEnvelope, archivedEnvelope)
   156  }
   157  
   158  func (s *MailserverSuite) TestManageLimits() {
   159  	err := s.server.Init(s.shh, s.config)
   160  	s.NoError(err)
   161  	s.server.ms.rateLimiter = newRateLimiter(time.Duration(5) * time.Millisecond)
   162  	s.False(s.server.ms.exceedsPeerRequests(types.BytesToHash([]byte("peerID"))))
   163  	s.Equal(1, len(s.server.ms.rateLimiter.db))
   164  	firstSaved := s.server.ms.rateLimiter.db["peerID"]
   165  
   166  	// second call when limit is not accomplished does not store a new limit
   167  	s.True(s.server.ms.exceedsPeerRequests(types.BytesToHash([]byte("peerID"))))
   168  	s.Equal(1, len(s.server.ms.rateLimiter.db))
   169  	s.Equal(firstSaved, s.server.ms.rateLimiter.db["peerID"])
   170  }
   171  
   172  func (s *MailserverSuite) TestDBKey() {
   173  	var h types.Hash
   174  	var emptyTopic types.TopicType
   175  	i := uint32(time.Now().Unix())
   176  	k := NewDBKey(i, emptyTopic, h)
   177  	s.Equal(len(k.Bytes()), DBKeyLength, "wrong DB key length")
   178  	s.Equal(byte(i%0x100), k.Bytes()[3], "raw representation should be big endian")
   179  	s.Equal(byte(i/0x1000000), k.Bytes()[0], "big endian expected")
   180  }
   181  
   182  func (s *MailserverSuite) TestRequestPaginationLimit() {
   183  	s.setupServer(s.server)
   184  	defer s.server.Close()
   185  
   186  	var (
   187  		sentEnvelopes  []*wakucommon.Envelope
   188  		sentHashes     []common.Hash
   189  		receivedHashes []common.Hash
   190  		archiveKeys    []string
   191  	)
   192  
   193  	now := time.Now()
   194  	count := uint32(10)
   195  
   196  	for i := count; i > 0; i-- {
   197  		sentTime := now.Add(time.Duration(-i) * time.Second)
   198  		env, err := generateEnvelope(sentTime)
   199  		s.NoError(err)
   200  		s.server.Archive(env)
   201  		key := NewDBKey(env.Expiry-env.TTL, types.TopicType(env.Topic), types.Hash(env.Hash()))
   202  		archiveKeys = append(archiveKeys, fmt.Sprintf("%x", key.Cursor()))
   203  		sentEnvelopes = append(sentEnvelopes, env)
   204  		sentHashes = append(sentHashes, env.Hash())
   205  	}
   206  
   207  	reqLimit := uint32(6)
   208  	peerID, request, err := s.prepareRequest(sentEnvelopes, reqLimit)
   209  	s.NoError(err)
   210  	payload, err := s.server.decompositeRequest(peerID, request)
   211  	s.NoError(err)
   212  	s.Nil(payload.Cursor)
   213  	s.Equal(reqLimit, payload.Limit)
   214  
   215  	receivedHashes, cursor, _ := processRequestAndCollectHashes(s.server, payload)
   216  
   217  	// 10 envelopes sent
   218  	s.Equal(count, uint32(len(sentEnvelopes)))
   219  	// 6 envelopes received
   220  	s.Len(receivedHashes, int(payload.Limit))
   221  	// the 6 envelopes received should be in forward order
   222  	s.Equal(sentHashes[:payload.Limit], receivedHashes)
   223  	// cursor should be the key of the last envelope of the last page
   224  	s.Equal(archiveKeys[payload.Limit-1], fmt.Sprintf("%x", cursor))
   225  
   226  	// second page
   227  	payload.Cursor = cursor
   228  	receivedHashes, cursor, _ = processRequestAndCollectHashes(s.server, payload)
   229  
   230  	// 4 envelopes received
   231  	s.Equal(int(count-payload.Limit), len(receivedHashes))
   232  	// cursor is nil because there are no other pages
   233  	s.Nil(cursor)
   234  }
   235  
   236  func (s *MailserverSuite) TestMailServer() {
   237  	s.setupServer(s.server)
   238  	defer s.server.Close()
   239  
   240  	env, err := generateEnvelope(time.Now())
   241  	s.NoError(err)
   242  
   243  	s.server.Archive(env)
   244  
   245  	testCases := []struct {
   246  		params *ServerTestParams
   247  		expect bool
   248  		isOK   bool
   249  		info   string
   250  	}{
   251  		{
   252  			params: s.defaultServerParams(env),
   253  			expect: true,
   254  			isOK:   true,
   255  			info:   "Processing a request where from and to are equal to an existing register, should provide results",
   256  		},
   257  		{
   258  			params: func() *ServerTestParams {
   259  				params := s.defaultServerParams(env)
   260  				params.low = params.birth + 1
   261  				params.upp = params.birth + 1
   262  
   263  				return params
   264  			}(),
   265  			expect: false,
   266  			isOK:   true,
   267  			info:   "Processing a request where from and to are greater than any existing register, should not provide results",
   268  		},
   269  		{
   270  			params: func() *ServerTestParams {
   271  				params := s.defaultServerParams(env)
   272  				params.upp = params.birth + 1
   273  				params.topic[0] = 0xFF
   274  
   275  				return params
   276  			}(),
   277  			expect: false,
   278  			isOK:   true,
   279  			info:   "Processing a request where to is greater than any existing register and with a specific topic, should not provide results",
   280  		},
   281  		{
   282  			params: func() *ServerTestParams {
   283  				params := s.defaultServerParams(env)
   284  				params.low = params.birth
   285  				params.upp = params.birth - 1
   286  
   287  				return params
   288  			}(),
   289  			isOK: false,
   290  			info: "Processing a request where to is lower than from should fail",
   291  		},
   292  		{
   293  			params: func() *ServerTestParams {
   294  				params := s.defaultServerParams(env)
   295  				params.low = 0
   296  				params.upp = params.birth + 24
   297  
   298  				return params
   299  			}(),
   300  			isOK: false,
   301  			info: "Processing a request where difference between from and to is > 24 should fail",
   302  		},
   303  	}
   304  	for _, testCase := range testCases {
   305  		// to satisfy gosec: C601 checks
   306  		tc := testCase
   307  		s.T().Run(tc.info, func(*testing.T) {
   308  			request := s.createRequest(tc.params)
   309  			src := crypto.FromECDSAPub(&tc.params.key.PublicKey)
   310  			payload, err := s.server.decompositeRequest(src, request)
   311  			s.Equal(tc.isOK, err == nil)
   312  			if err == nil {
   313  				s.Equal(tc.params.low, payload.Lower)
   314  				s.Equal(tc.params.upp, payload.Upper)
   315  				s.Equal(tc.params.limit, payload.Limit)
   316  				s.Equal(types.TopicToBloom(tc.params.topic), payload.Bloom)
   317  				s.Equal(tc.expect, s.messageExists(env, tc.params.low, tc.params.upp, payload.Bloom, tc.params.limit))
   318  
   319  				src[0]++
   320  				_, err = s.server.decompositeRequest(src, request)
   321  				s.True(err == nil)
   322  			}
   323  		})
   324  	}
   325  }
   326  
   327  func (s *MailserverSuite) TestDecodeRequest() {
   328  	s.setupServer(s.server)
   329  	defer s.server.Close()
   330  
   331  	payload := MessagesRequestPayload{
   332  		Lower:  50,
   333  		Upper:  100,
   334  		Bloom:  []byte{0x01},
   335  		Topics: [][]byte{},
   336  		Limit:  10,
   337  		Cursor: []byte{},
   338  		Batch:  true,
   339  	}
   340  	data, err := rlp.EncodeToBytes(payload)
   341  	s.Require().NoError(err)
   342  
   343  	id, err := s.shh.NewKeyPair()
   344  	s.Require().NoError(err)
   345  	srcKey, err := s.shh.GetPrivateKey(id)
   346  	s.Require().NoError(err)
   347  
   348  	env := s.createEnvelope(types.TopicType{0x01}, data, srcKey)
   349  
   350  	decodedPayload, err := s.server.decodeRequest(nil, env)
   351  	s.Require().NoError(err)
   352  	s.Equal(payload, decodedPayload)
   353  }
   354  
   355  func (s *MailserverSuite) TestDecodeRequestNoUpper() {
   356  	s.setupServer(s.server)
   357  	defer s.server.Close()
   358  
   359  	payload := MessagesRequestPayload{
   360  		Lower:  50,
   361  		Bloom:  []byte{0x01},
   362  		Limit:  10,
   363  		Cursor: []byte{},
   364  		Batch:  true,
   365  	}
   366  	data, err := rlp.EncodeToBytes(payload)
   367  	s.Require().NoError(err)
   368  
   369  	id, err := s.shh.NewKeyPair()
   370  	s.Require().NoError(err)
   371  	srcKey, err := s.shh.GetPrivateKey(id)
   372  	s.Require().NoError(err)
   373  
   374  	env := s.createEnvelope(types.TopicType{0x01}, data, srcKey)
   375  
   376  	decodedPayload, err := s.server.decodeRequest(nil, env)
   377  	s.Require().NoError(err)
   378  	s.NotEqual(0, decodedPayload.Upper)
   379  }
   380  
   381  func (s *MailserverSuite) TestProcessRequestDeadlockHandling() {
   382  	s.setupServer(s.server)
   383  	defer s.server.Close()
   384  
   385  	var archievedEnvelopes []*wakucommon.Envelope
   386  
   387  	now := time.Now()
   388  	count := uint32(10)
   389  
   390  	// Archieve some envelopes.
   391  	for i := count; i > 0; i-- {
   392  		sentTime := now.Add(time.Duration(-i) * time.Second)
   393  		env, err := generateEnvelope(sentTime)
   394  		s.NoError(err)
   395  		s.server.Archive(env)
   396  		archievedEnvelopes = append(archievedEnvelopes, env)
   397  	}
   398  
   399  	// Prepare a request.
   400  	peerID, request, err := s.prepareRequest(archievedEnvelopes, 5)
   401  	s.NoError(err)
   402  	payload, err := s.server.decompositeRequest(peerID, request)
   403  	s.NoError(err)
   404  
   405  	testCases := []struct {
   406  		Name    string
   407  		Timeout time.Duration
   408  		Verify  func(
   409  			Iterator,
   410  			time.Duration, // processRequestInBundles timeout
   411  			chan []rlp.RawValue,
   412  		)
   413  	}{
   414  		{
   415  			Name:    "finish processing using `done` channel",
   416  			Timeout: time.Second * 5,
   417  			Verify: func(
   418  				iter Iterator,
   419  				timeout time.Duration,
   420  				bundles chan []rlp.RawValue,
   421  			) {
   422  				done := make(chan struct{})
   423  				processFinished := make(chan struct{})
   424  
   425  				go func() {
   426  					s.server.ms.processRequestInBundles(iter, payload.Bloom, payload.Topics, int(payload.Limit), timeout, "req-01", bundles, done)
   427  					close(processFinished)
   428  				}()
   429  				go close(done)
   430  
   431  				select {
   432  				case <-processFinished:
   433  				case <-time.After(time.Second):
   434  					s.FailNow("waiting for processing finish timed out")
   435  				}
   436  			},
   437  		},
   438  		{
   439  			Name:    "finish processing due to timeout",
   440  			Timeout: time.Second,
   441  			Verify: func(
   442  				iter Iterator,
   443  				timeout time.Duration,
   444  				bundles chan []rlp.RawValue,
   445  			) {
   446  				done := make(chan struct{}) // won't be closed because we test timeout of `processRequestInBundles()`
   447  				processFinished := make(chan struct{})
   448  
   449  				go func() {
   450  					s.server.ms.processRequestInBundles(iter, payload.Bloom, payload.Topics, int(payload.Limit), time.Second, "req-01", bundles, done)
   451  					close(processFinished)
   452  				}()
   453  
   454  				select {
   455  				case <-processFinished:
   456  				case <-time.After(time.Second * 5):
   457  					s.FailNow("waiting for processing finish timed out")
   458  				}
   459  			},
   460  		},
   461  	}
   462  
   463  	for _, tc := range testCases {
   464  		s.T().Run(tc.Name, func(t *testing.T) {
   465  			iter, err := s.server.ms.createIterator(payload)
   466  			s.Require().NoError(err)
   467  
   468  			defer func() { _ = iter.Release() }()
   469  
   470  			// Nothing reads from this unbuffered channel which simulates a situation
   471  			// when a connection between a peer and mail server was dropped.
   472  			bundles := make(chan []rlp.RawValue)
   473  
   474  			tc.Verify(iter, tc.Timeout, bundles)
   475  		})
   476  	}
   477  }
   478  
   479  func (s *MailserverSuite) messageExists(envelope *wakucommon.Envelope, low, upp uint32, bloom []byte, limit uint32) bool {
   480  	receivedHashes, _, _ := processRequestAndCollectHashes(s.server, MessagesRequestPayload{
   481  		Lower: low,
   482  		Upper: upp,
   483  		Bloom: bloom,
   484  		Limit: limit,
   485  	})
   486  	for _, hash := range receivedHashes {
   487  		if hash == envelope.Hash() {
   488  			return true
   489  		}
   490  	}
   491  	return false
   492  }
   493  
   494  func (s *MailserverSuite) setupServer(server *WakuMailServer) {
   495  	const password = "password_for_this_test"
   496  
   497  	s.shh = waku.New(&waku.DefaultConfig, nil)
   498  	s.shh.RegisterMailServer(server)
   499  
   500  	err := server.Init(s.shh, &params.WakuConfig{
   501  		DataDir:            s.dataDir,
   502  		MailServerPassword: password,
   503  		MinimumPoW:         powRequirement,
   504  	})
   505  	if err != nil {
   506  		s.T().Fatal(err)
   507  	}
   508  
   509  	keyID, err = s.shh.AddSymKeyFromPassword(password)
   510  	if err != nil {
   511  		s.T().Fatalf("failed to create symmetric key for mail request: %s", err)
   512  	}
   513  }
   514  
   515  func (s *MailserverSuite) prepareRequest(envelopes []*wakucommon.Envelope, limit uint32) (
   516  	[]byte, *wakucommon.Envelope, error,
   517  ) {
   518  	if len(envelopes) == 0 {
   519  		return nil, nil, errors.New("envelopes is empty")
   520  	}
   521  
   522  	now := time.Now()
   523  
   524  	params := s.defaultServerParams(envelopes[0])
   525  	params.low = uint32(now.Add(time.Duration(-len(envelopes)) * time.Second).Unix())
   526  	params.upp = uint32(now.Unix())
   527  	params.limit = limit
   528  
   529  	request := s.createRequest(params)
   530  	peerID := crypto.FromECDSAPub(&params.key.PublicKey)
   531  
   532  	return peerID, request, nil
   533  }
   534  
   535  func (s *MailserverSuite) defaultServerParams(env *wakucommon.Envelope) *ServerTestParams {
   536  	id, err := s.shh.NewKeyPair()
   537  	if err != nil {
   538  		s.T().Fatalf("failed to generate new key pair with seed %d: %s.", seed, err)
   539  	}
   540  	testPeerID, err := s.shh.GetPrivateKey(id)
   541  	if err != nil {
   542  		s.T().Fatalf("failed to retrieve new key pair with seed %d: %s.", seed, err)
   543  	}
   544  	birth := env.Expiry - env.TTL
   545  
   546  	return &ServerTestParams{
   547  		topic: types.TopicType(env.Topic),
   548  		birth: birth,
   549  		low:   birth - 1,
   550  		upp:   birth + 1,
   551  		limit: 0,
   552  		key:   testPeerID,
   553  	}
   554  }
   555  
   556  func (s *MailserverSuite) createRequest(p *ServerTestParams) *wakucommon.Envelope {
   557  	bloom := types.TopicToBloom(p.topic)
   558  	data := make([]byte, 8)
   559  	binary.BigEndian.PutUint32(data, p.low)
   560  	binary.BigEndian.PutUint32(data[4:], p.upp)
   561  	data = append(data, bloom...)
   562  
   563  	if p.limit != 0 {
   564  		limitData := make([]byte, 4)
   565  		binary.BigEndian.PutUint32(limitData, p.limit)
   566  		data = append(data, limitData...)
   567  	}
   568  
   569  	return s.createEnvelope(p.topic, data, p.key)
   570  }
   571  
   572  func (s *MailserverSuite) createEnvelope(topic types.TopicType, data []byte, srcKey *ecdsa.PrivateKey) *wakucommon.Envelope {
   573  	key, err := s.shh.GetSymKey(keyID)
   574  	if err != nil {
   575  		s.T().Fatalf("failed to retrieve sym key with seed %d: %s.", seed, err)
   576  	}
   577  
   578  	params := &wakucommon.MessageParams{
   579  		KeySym:   key,
   580  		Topic:    wakucommon.TopicType(topic),
   581  		Payload:  data,
   582  		PoW:      powRequirement * 2,
   583  		WorkTime: 2,
   584  		Src:      srcKey,
   585  	}
   586  
   587  	msg, err := wakucommon.NewSentMessage(params)
   588  	if err != nil {
   589  		s.T().Fatalf("failed to create new message with seed %d: %s.", seed, err)
   590  	}
   591  
   592  	env, err := msg.Wrap(params, time.Now())
   593  	if err != nil {
   594  		s.T().Fatalf("failed to wrap with seed %d: %s.", seed, err)
   595  	}
   596  	return env
   597  }
   598  
   599  func generateEnvelopeWithKeys(sentTime time.Time, keySym []byte, keyAsym *ecdsa.PublicKey) (*wakucommon.Envelope, error) {
   600  	params := &wakucommon.MessageParams{
   601  		Topic:    wakucommon.TopicType{0x1F, 0x7E, 0xA1, 0x7F},
   602  		Payload:  testPayload,
   603  		PoW:      powRequirement,
   604  		WorkTime: 2,
   605  	}
   606  
   607  	if len(keySym) > 0 {
   608  		params.KeySym = keySym
   609  	} else if keyAsym != nil {
   610  		params.Dst = keyAsym
   611  	}
   612  
   613  	msg, err := wakucommon.NewSentMessage(params)
   614  	if err != nil {
   615  		return nil, fmt.Errorf("failed to create new message with seed %d: %s", seed, err)
   616  	}
   617  	env, err := msg.Wrap(params, sentTime)
   618  	if err != nil {
   619  		return nil, fmt.Errorf("failed to wrap with seed %d: %s", seed, err)
   620  	}
   621  
   622  	return env, nil
   623  }
   624  
   625  func generateEnvelope(sentTime time.Time) (*wakucommon.Envelope, error) {
   626  	h := crypto.Keccak256Hash([]byte("test sample data"))
   627  	return generateEnvelopeWithKeys(sentTime, h[:], nil)
   628  }
   629  
   630  func processRequestAndCollectHashes(server *WakuMailServer, payload MessagesRequestPayload) ([]common.Hash, []byte, types.Hash) {
   631  	iter, _ := server.ms.createIterator(payload)
   632  	defer func() { _ = iter.Release() }()
   633  	bundles := make(chan []rlp.RawValue, 10)
   634  	done := make(chan struct{})
   635  
   636  	var hashes []common.Hash
   637  	go func() {
   638  		for bundle := range bundles {
   639  			for _, rawEnvelope := range bundle {
   640  				var env *wakucommon.Envelope
   641  				if err := rlp.DecodeBytes(rawEnvelope, &env); err != nil {
   642  					panic(err)
   643  				}
   644  				hashes = append(hashes, env.Hash())
   645  			}
   646  		}
   647  		close(done)
   648  	}()
   649  
   650  	cursor, lastHash := server.ms.processRequestInBundles(iter, payload.Bloom, payload.Topics, int(payload.Limit), time.Minute, "req-01", bundles, done)
   651  
   652  	<-done
   653  
   654  	return hashes, cursor, lastHash
   655  }