Skip to content

Commit f1f8897

Browse files
committed
neutrino + query: query.ErrChan, jobResults, Request.HandleResp refactor
- Added Unfinished bool to jobResult to indicate successful jobs that still need to send another request to the peer to be considered complete. - Made ErrChan a query option in that way it is optional for different queries. - Refactored HandleResp, peer is now passed as query.Peer instead of using its address, jobErr is also passed as an argument in the HandleResp function. This refactor is useful to enable the query package to be used for fetching block headers. We passed the peer as the peer is needed for validation and jobErr, so as to quickly exit the feedback loop when fetched headers do not pass preliminary verification. Signed-off-by: Maureen Ononiwu <[email protected]>
1 parent 2748d37 commit f1f8897

File tree

9 files changed

+549
-107
lines changed

9 files changed

+549
-107
lines changed

blockmanager.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -850,8 +850,14 @@ func (c *checkpointedCFHeadersQuery) requests() []*query.Request {
850850

851851
// handleResponse is the internal response handler used for requests for this
852852
// CFHeaders query.
853-
func (c *checkpointedCFHeadersQuery) handleResponse(req, resp wire.Message,
854-
peerAddr string) query.Progress {
853+
func (c *checkpointedCFHeadersQuery) handleResponse(request query.ReqMessage, resp wire.Message,
854+
peer query.Peer, _ *error) query.Progress {
855+
856+
peerAddr := ""
857+
if peer != nil {
858+
peerAddr = peer.Addr()
859+
}
860+
req := request.Message()
855861

856862
r, ok := resp.(*wire.MsgCFHeaders)
857863
if !ok {
@@ -1106,7 +1112,7 @@ func (b *blockManager) getCheckpointedCFHeaders(checkpoints []*chainhash.Hash,
11061112
// Hand the queries to the work manager, and consume the verified
11071113
// responses as they come back.
11081114
errChan := b.cfg.QueryDispatcher.Query(
1109-
q.requests(), query.Cancel(b.quit), query.NoRetryMax(),
1115+
q.requests(), query.Cancel(b.quit), query.NoRetryMax(), query.ErrChan(make(chan error, 1)),
11101116
)
11111117

11121118
// Keep waiting for more headers as long as we haven't received an

blockmanager_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,14 +214,14 @@ func generateHeaders(genesisBlockHeader *wire.BlockHeader,
214214

215215
// generateResponses generates the MsgCFHeaders messages from the given queries
216216
// and headers.
217-
func generateResponses(msgs []wire.Message,
217+
func generateResponses(msgs []query.ReqMessage,
218218
headers *headers) ([]*wire.MsgCFHeaders, error) {
219219

220220
// Craft a response for each message.
221221
var responses []*wire.MsgCFHeaders
222222
for _, msg := range msgs {
223223
// Only GetCFHeaders expected.
224-
q, ok := msg.(*wire.MsgGetCFHeaders)
224+
q, ok := msg.Message().(*wire.MsgGetCFHeaders)
225225
if !ok {
226226
return nil, fmt.Errorf("got unexpected message %T",
227227
msg)
@@ -350,9 +350,9 @@ func TestBlockManagerInitialInterval(t *testing.T) {
350350
requests []*query.Request,
351351
options ...query.QueryOption) chan error {
352352

353-
var msgs []wire.Message
353+
var msgs []query.ReqMessage
354354
for _, q := range requests {
355-
msgs = append(msgs, q.Req.Message())
355+
msgs = append(msgs, q.Req)
356356
}
357357

358358
responses, err := generateResponses(msgs, headers)
@@ -378,8 +378,9 @@ func TestBlockManagerInitialInterval(t *testing.T) {
378378

379379
// Let the blockmanager handle the
380380
// message.
381+
var jobErr error
381382
progress := requests[index].HandleResp(
382-
msgs[index], &resp, "",
383+
msgs[index], &resp, nil, &jobErr,
383384
)
384385

385386
if !progress.Finished {
@@ -400,7 +401,7 @@ func TestBlockManagerInitialInterval(t *testing.T) {
400401
// Otherwise resend the response we
401402
// just sent.
402403
progress = requests[index].HandleResp(
403-
msgs[index], &resp2, "",
404+
msgs[index], &resp2, nil, &jobErr,
404405
)
405406
if !progress.Finished {
406407
errChan <- fmt.Errorf("got "+
@@ -580,9 +581,9 @@ func TestBlockManagerInvalidInterval(t *testing.T) {
580581
requests []*query.Request,
581582
options ...query.QueryOption) chan error {
582583

583-
var msgs []wire.Message
584+
var msgs []query.ReqMessage
584585
for _, q := range requests {
585-
msgs = append(msgs, q.Req.Message())
586+
msgs = append(msgs, q.Req)
586587
}
587588
responses, err := generateResponses(msgs, headers)
588589
require.NoError(t, err)
@@ -617,9 +618,10 @@ func TestBlockManagerInvalidInterval(t *testing.T) {
617618
go func() {
618619
// Check that the success of the callback match what we
619620
// expect.
621+
var jobErr error
620622
for i := range responses {
621623
progress := requests[i].HandleResp(
622-
msgs[i], responses[i], "",
624+
msgs[i], responses[i], nil, &jobErr,
623625
)
624626
if i == test.firstInvalid {
625627
if progress.Finished {

query.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -470,9 +470,10 @@ func (q *cfiltersQuery) request() *query.Request {
470470

471471
// handleResponse validates that the cfilter response we get from a peer is
472472
// sane given the getcfilter query that we made.
473-
func (q *cfiltersQuery) handleResponse(req, resp wire.Message,
474-
_ string) query.Progress {
473+
func (q *cfiltersQuery) handleResponse(r query.ReqMessage, resp wire.Message,
474+
peer query.Peer, _ *error) query.Progress {
475475

476+
req := r.Message()
476477
// The request must have been a "getcfilters" msg.
477478
request, ok := req.(*wire.MsgGetCFilters)
478479
if !ok {
@@ -784,6 +785,7 @@ func (s *ChainService) GetCFilter(blockHash chainhash.Hash,
784785
query.Cancel(s.quit),
785786
query.Encoding(qo.encoding),
786787
query.NumRetries(qo.numRetries),
788+
query.ErrChan(make(chan error, 1)),
787789
}
788790

789791
errChan := s.workManager.Query(
@@ -868,7 +870,12 @@ func (s *ChainService) GetBlock(blockHash chainhash.Hash,
868870
// handleResp will be called for each message received from a peer. It
869871
// will be used to signal to the work manager whether progress has been
870872
// made or not.
871-
handleResp := func(req, resp wire.Message, peer string) query.Progress {
873+
handleResp := func(request query.ReqMessage, resp wire.Message, sp query.Peer, _ *error) query.Progress {
874+
req := request.Message()
875+
peer := ""
876+
if sp != nil {
877+
peer = sp.Addr()
878+
}
872879
// The request must have been a "getdata" msg.
873880
_, ok := req.(*wire.MsgGetData)
874881
if !ok {
@@ -968,6 +975,7 @@ func (s *ChainService) GetBlock(blockHash chainhash.Hash,
968975
query.Encoding(qo.encoding),
969976
query.NumRetries(qo.numRetries),
970977
query.Cancel(s.quit),
978+
query.ErrChan(make(chan error, 1)),
971979
}
972980

973981
// Send the request to the work manager and await a response.

query/interface.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ type queryOptions struct {
4343
// that a query can be retried. If this is set then numRetries has no
4444
// effect.
4545
noRetryMax bool
46+
47+
// errChan error channel with which the workmananger sends error.
48+
errChan chan error
4649
}
4750

4851
// QueryOption is a functional option argument to any of the network query
@@ -67,6 +70,14 @@ func (qo *queryOptions) applyQueryOptions(options ...QueryOption) {
6770
}
6871
}
6972

73+
// ErrChan is a query option that specifies the error channel which the workmanager
74+
// sends any error to.
75+
func ErrChan(err chan error) QueryOption {
76+
return func(qo *queryOptions) {
77+
qo.errChan = err
78+
}
79+
}
80+
7081
// NumRetries is a query option that specifies the number of times a query
7182
// should be retried.
7283
func NumRetries(num uint8) QueryOption {
@@ -138,7 +149,7 @@ type Request struct {
138149
// should validate the response and immediately return the progress.
139150
// The response should be handed off to another goroutine for
140151
// processing.
141-
HandleResp func(req, resp wire.Message, peer string) Progress
152+
HandleResp func(req ReqMessage, resp wire.Message, peer Peer, jobErr *error) Progress
142153

143154
// SendQuery handles sending request to the worker's peer. It returns an error,
144155
// if one is encountered while sending the request.

query/worker.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ var (
1717
// ErrJobCanceled is returned if the job is canceled before the query
1818
// has been answered.
1919
ErrJobCanceled = errors.New("job canceled")
20+
21+
// ErrResponseExistForQuery is returned if response exists already for the query.
22+
ErrResponseExistForQuery = errors.New("response Exists for query")
23+
24+
// ErrResponseErr is returned if we received a compatible response for the query but, it did not pass
25+
// preliminary verification.
26+
ErrResponseErr = errors.New("received response with error")
2027
)
2128

2229
// queryJob is the internal struct that wraps the Query to work on, in
@@ -42,9 +49,10 @@ func (q *queryJob) Index() float64 {
4249

4350
// jobResult is the final result of the worker's handling of the queryJob.
4451
type jobResult struct {
45-
job *queryJob
46-
peer Peer
47-
err error
52+
job *queryJob
53+
peer Peer
54+
err error
55+
unfinished bool
4856
}
4957

5058
// worker is responsible for polling work from its work queue, and handing it
@@ -152,8 +160,9 @@ nexJobLoop:
152160
// Wait for the correct response to be received from the peer,
153161
// or an error happening.
154162
var (
155-
jobErr error
156-
timeout = time.NewTimer(job.timeout)
163+
jobErr error
164+
jobUnfinished bool
165+
timeout = time.NewTimer(job.timeout)
157166
)
158167

159168
feedbackLoop:
@@ -164,7 +173,7 @@ nexJobLoop:
164173
// our request.
165174
case resp := <-msgChan:
166175
progress := job.HandleResp(
167-
job.Req.Message(), resp, peer.Addr(),
176+
job.Req, resp, peer, &jobErr,
168177
)
169178

170179
log.Tracef("Worker %v handled msg %T while "+
@@ -176,6 +185,10 @@ nexJobLoop:
176185
// If the response did not answer our query, we
177186
// check whether it did progress it.
178187
if !progress.Finished {
188+
if jobErr != nil {
189+
break feedbackLoop
190+
}
191+
179192
// If it did make progress we reset the
180193
// timeout. This ensures that the
181194
// queries with multiple responses
@@ -192,6 +205,9 @@ nexJobLoop:
192205
continue feedbackLoop
193206
}
194207

208+
if !progress.Progressed {
209+
jobUnfinished = true
210+
}
195211
// We did get a valid response, and can break
196212
// the loop.
197213
break feedbackLoop
@@ -260,9 +276,10 @@ nexJobLoop:
260276
// getting a new job.
261277
select {
262278
case results <- &jobResult{
263-
job: resultJob,
264-
peer: peer,
265-
err: jobErr,
279+
job: resultJob,
280+
peer: peer,
281+
err: jobErr,
282+
unfinished: jobUnfinished,
266283
}:
267284
case <-quit:
268285
return

0 commit comments

Comments
 (0)