Skip to content

Commit

Permalink
Merge pull request #3785 from Roasbeef/gossip-queries-fix
Browse files Browse the repository at this point in the history
discovery: properly set FirstBlockHeight and NumBlocks in responses
  • Loading branch information
Roasbeef committed Dec 11, 2019
2 parents a68144b + 6a9b961 commit a7c2b12
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 17 deletions.
34 changes: 30 additions & 4 deletions discovery/syncer.go
Expand Up @@ -824,6 +824,14 @@ func (g *GossipSyncer) replyChanRangeQuery(query *lnwire.QueryChannelRange) erro
// TODO(roasbeef): means can't send max uint above?
// * or make internal 64

// In the base case (no actual response) the first block and the last
// block in the query will be the same. In the loop below, we'll update
// these two variables incrementally with each chunk to properly
// compute the starting block for each response and the number of
// blocks in a response.
firstBlockHeight := query.FirstBlockHeight
lastBlockHeight := query.FirstBlockHeight

numChannels := int32(len(channelRange))
numChansSent := int32(0)
for {
Expand Down Expand Up @@ -854,13 +862,31 @@ func (g *GossipSyncer) replyChanRangeQuery(query *lnwire.QueryChannelRange) erro
"size=%v", g.cfg.peerPub[:], len(channelChunk))
}

// If we have any channels at all to return, then we need to
// update our pointers to the first and last blocks for each
// response.
if len(channelChunk) > 0 {
firstBlockHeight = channelChunk[0].BlockHeight
lastBlockHeight = channelChunk[len(channelChunk)-1].BlockHeight
}

// The number of blocks contained in this response (the total
// span) is the difference between the last channel ID and the
// first in the range. We add one as even if all channels
// returned are in the same block, we need to count that.
numBlocksInResp := lastBlockHeight - firstBlockHeight + 1

// With our chunk assembled, we'll now send to the remote peer
// the current chunk.
replyChunk := lnwire.ReplyChannelRange{
QueryChannelRange: *query,
Complete: 0,
EncodingType: g.cfg.encodingType,
ShortChanIDs: channelChunk,
QueryChannelRange: lnwire.QueryChannelRange{
ChainHash: query.ChainHash,
NumBlocks: numBlocksInResp,
FirstBlockHeight: firstBlockHeight,
},
Complete: 0,
EncodingType: g.cfg.encodingType,
ShortChanIDs: channelChunk,
}
if isFinalChunk {
replyChunk.Complete = 1
Expand Down
73 changes: 60 additions & 13 deletions discovery/syncer_test.go
Expand Up @@ -709,20 +709,31 @@ func TestGossipSyncerReplyChanRangeQuery(t *testing.T) {

// Next, we'll craft a query to ask for all the new chan ID's after
// block 100.
const startingBlockHeight int = 100
query := &lnwire.QueryChannelRange{
FirstBlockHeight: 100,
FirstBlockHeight: uint32(startingBlockHeight),
NumBlocks: 50,
}

// We'll then launch a goroutine to reply to the query with a set of 5
// responses. This will ensure we get two full chunks, and one partial
// chunk.
resp := []lnwire.ShortChannelID{
lnwire.NewShortChanIDFromInt(1),
lnwire.NewShortChanIDFromInt(2),
lnwire.NewShortChanIDFromInt(3),
lnwire.NewShortChanIDFromInt(4),
lnwire.NewShortChanIDFromInt(5),
queryResp := []lnwire.ShortChannelID{
{
BlockHeight: uint32(startingBlockHeight),
},
{
BlockHeight: 102,
},
{
BlockHeight: 104,
},
{
BlockHeight: 106,
},
{
BlockHeight: 108,
},
}

errCh := make(chan error, 1)
Expand All @@ -740,7 +751,7 @@ func TestGossipSyncerReplyChanRangeQuery(t *testing.T) {

// If the proper request was sent, then we'll respond
// with our set of short channel ID's.
chanSeries.filterRangeResp <- resp
chanSeries.filterRangeResp <- queryResp
errCh <- nil
}
}()
Expand All @@ -767,16 +778,52 @@ func TestGossipSyncerReplyChanRangeQuery(t *testing.T) {
t.Fatalf("expected ReplyChannelRange instead got %T", msg)
}

// Only for the first iteration do we set the offset to
// zero as no chunks have been processed yet. For every
// other iteration, we want to move forward by the
// chunkSize (from the staring block height).
offset := 0
if i != 0 {
offset = 1
}
expectedFirstBlockHeight := (i+offset)*2 + startingBlockHeight

switch {
// If this is not the last chunk, then Complete should
// be set to zero. Otherwise, it should be one.
switch {
case i < 2 && rangeResp.Complete != 0:
t.Fatalf("non-final chunk should have "+
"Complete=0: %v", spew.Sdump(rangeResp))

case i < 2 && rangeResp.NumBlocks != chunkSize+1:
t.Fatalf("NumBlocks fields in resp "+
"incorrect: expected %v got %v",
chunkSize+1, rangeResp.NumBlocks)

case i < 2 && rangeResp.FirstBlockHeight !=
uint32(expectedFirstBlockHeight):

t.Fatalf("FirstBlockHeight incorrect: "+
"expected %v got %v",
rangeResp.FirstBlockHeight,
expectedFirstBlockHeight)
case i == 2 && rangeResp.Complete != 1:
t.Fatalf("final chunk should have "+
"Complete=1: %v", spew.Sdump(rangeResp))

case i == 2 && rangeResp.NumBlocks != 1:
t.Fatalf("NumBlocks fields in resp "+
"incorrect: expected %v got %v", 1,
rangeResp.NumBlocks)

case i == 2 && rangeResp.FirstBlockHeight !=
queryResp[len(queryResp)-1].BlockHeight:

t.Fatalf("FirstBlockHeight incorrect: "+
"expected %v got %v",
rangeResp.FirstBlockHeight,
queryResp[len(queryResp)-1].BlockHeight)

}

respMsgs = append(respMsgs, rangeResp.ShortChanIDs...)
Expand All @@ -785,13 +832,13 @@ func TestGossipSyncerReplyChanRangeQuery(t *testing.T) {

// We should get back exactly 5 short chan ID's, and they should match
// exactly the ID's we sent as a reply.
if len(respMsgs) != len(resp) {
if len(respMsgs) != len(queryResp) {
t.Fatalf("expected %v chan ID's, instead got %v",
len(resp), spew.Sdump(respMsgs))
len(queryResp), spew.Sdump(respMsgs))
}
if !reflect.DeepEqual(resp, respMsgs) {
if !reflect.DeepEqual(queryResp, respMsgs) {
t.Fatalf("mismatched response: expected %v, got %v",
spew.Sdump(resp), spew.Sdump(respMsgs))
spew.Sdump(queryResp), spew.Sdump(respMsgs))
}

// Wait for error from goroutine.
Expand Down

0 comments on commit a7c2b12

Please sign in to comment.