Merge pull request #4395 from bmancini55/reply-channel-range-query
discovery: prevent endBlock overflow in replyChanRangeQuery
This commit is contained in:
commit
8c79359247
@ -918,7 +918,7 @@ func (g *GossipSyncer) replyChanRangeQuery(query *lnwire.QueryChannelRange) erro
|
|||||||
// Next, we'll consult the time series to obtain the set of known
|
// Next, we'll consult the time series to obtain the set of known
|
||||||
// channel ID's that match their query.
|
// channel ID's that match their query.
|
||||||
startBlock := query.FirstBlockHeight
|
startBlock := query.FirstBlockHeight
|
||||||
endBlock := startBlock + query.NumBlocks - 1
|
endBlock := query.LastBlockHeight()
|
||||||
channelRange, err := g.cfg.channelSeries.FilterChannelRange(
|
channelRange, err := g.cfg.channelSeries.FilterChannelRange(
|
||||||
query.ChainHash, startBlock, endBlock,
|
query.ChainHash, startBlock, endBlock,
|
||||||
)
|
)
|
||||||
|
@ -915,6 +915,118 @@ func TestGossipSyncerReplyChanRangeQuery(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestGossipSyncerReplyChanRangeQuery tests a variety of
|
||||||
|
// QueryChannelRange messages to ensure the underlying queries are
|
||||||
|
// executed with the correct block range
|
||||||
|
func TestGossipSyncerReplyChanRangeQueryBlockRange(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// First create our test gossip syncer that will handle and
|
||||||
|
// respond to the test queries
|
||||||
|
_, syncer, chanSeries := newTestSyncer(
|
||||||
|
lnwire.NewShortChanIDFromInt(10), defaultEncoding, math.MaxInt32,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Next construct test queries with various startBlock and endBlock
|
||||||
|
// ranges
|
||||||
|
queryReqs := []*lnwire.QueryChannelRange{
|
||||||
|
// full range example
|
||||||
|
{
|
||||||
|
FirstBlockHeight: uint32(0),
|
||||||
|
NumBlocks: uint32(math.MaxUint32),
|
||||||
|
},
|
||||||
|
|
||||||
|
// small query example that does not overflow
|
||||||
|
{
|
||||||
|
FirstBlockHeight: uint32(1000),
|
||||||
|
NumBlocks: uint32(100),
|
||||||
|
},
|
||||||
|
|
||||||
|
// overflow example
|
||||||
|
{
|
||||||
|
FirstBlockHeight: uint32(1000),
|
||||||
|
NumBlocks: uint32(math.MaxUint32),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next construct the expected filterRangeReq startHeight and endHeight
|
||||||
|
// values that we will compare to the captured values
|
||||||
|
expFilterReqs := []filterRangeReq{
|
||||||
|
{
|
||||||
|
startHeight: uint32(0),
|
||||||
|
endHeight: uint32(math.MaxUint32 - 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
startHeight: uint32(1000),
|
||||||
|
endHeight: uint32(1099),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
startHeight: uint32(1000),
|
||||||
|
endHeight: uint32(math.MaxUint32),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll then launch a goroutine to capture the filterRangeReqs for
|
||||||
|
// each request and return those results once all queries have been
|
||||||
|
// received
|
||||||
|
resultsCh := make(chan []filterRangeReq, 1)
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
// We will capture the values supplied to the chanSeries here
|
||||||
|
// and return the results once all the requests have been
|
||||||
|
// collected
|
||||||
|
capFilterReqs := []filterRangeReq{}
|
||||||
|
|
||||||
|
for filterReq := range chanSeries.filterRangeReqs {
|
||||||
|
// capture the filter request so we can compare to the
|
||||||
|
// expected values later
|
||||||
|
capFilterReqs = append(capFilterReqs, filterReq)
|
||||||
|
|
||||||
|
// Reply with an empty result for each query to allow
|
||||||
|
// unblock the caller
|
||||||
|
queryResp := []lnwire.ShortChannelID{}
|
||||||
|
chanSeries.filterRangeResp <- queryResp
|
||||||
|
|
||||||
|
// Once we have collected all results send the results
|
||||||
|
// back to the main thread and terminate the goroutine
|
||||||
|
if len(capFilterReqs) == len(expFilterReqs) {
|
||||||
|
resultsCh <- capFilterReqs
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// We'll launch a goroutine to send the query sequentially. This
|
||||||
|
// goroutine ensures that the timeout logic below on the mainthread
|
||||||
|
// will be reached
|
||||||
|
go func() {
|
||||||
|
for _, query := range queryReqs {
|
||||||
|
if err := syncer.replyChanRangeQuery(query); err != nil {
|
||||||
|
errCh <- fmt.Errorf("unable to issue query: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for the results to be collected and validate that the
|
||||||
|
// collected results match the expected results, the timeout to
|
||||||
|
// expire, or an error to occur
|
||||||
|
select {
|
||||||
|
case capFilterReq := <-resultsCh:
|
||||||
|
if !reflect.DeepEqual(expFilterReqs, capFilterReq) {
|
||||||
|
t.Fatalf("mismatched filter reqs: expected %v, got %v",
|
||||||
|
spew.Sdump(expFilterReqs), spew.Sdump(capFilterReq))
|
||||||
|
}
|
||||||
|
case <-time.After(time.Second * 10):
|
||||||
|
t.Fatalf("goroutine did not return within 10 seconds")
|
||||||
|
case err := <-errCh:
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestGossipSyncerReplyChanRangeQueryNoNewChans tests that if we issue a reply
|
// TestGossipSyncerReplyChanRangeQueryNoNewChans tests that if we issue a reply
|
||||||
// for a channel range query, and we don't have any new channels, then we send
|
// for a channel range query, and we don't have any new channels, then we send
|
||||||
// back a single response that signals completion.
|
// back a single response that signals completion.
|
||||||
|
Loading…
Reference in New Issue
Block a user