discovery: cover requested range in ReplyChannelRange messages

In order to properly adhere to the spec, when handling a
QueryChannelRange message, we must reply with a series of
ReplyChannelRange messages, that when consumed together cover the
entirety of the block range requested.
This commit is contained in:
Wilmer Paulino 2019-12-13 16:08:10 -08:00
parent 1f781ea431
commit c7c0853531
No known key found for this signature in database
GPG Key ID: 6DF57B9F9514972F
2 changed files with 68 additions and 48 deletions

@ -825,13 +825,13 @@ 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
// In the base case (no actual response) the first block and last block
// will match those of the query. 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 := startBlock
lastBlockHeight := endBlock
numChannels := int32(len(channelRange))
numChansSent := int32(0)
@ -867,8 +867,25 @@ func (g *GossipSyncer) replyChanRangeQuery(query *lnwire.QueryChannelRange) erro
// 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
// If this is the first response we'll send, we'll point
// the first block to the first block in the query.
// Otherwise, we'll continue from the block we left off
// at.
if numChansSent == 0 {
firstBlockHeight = startBlock
} else {
firstBlockHeight = lastBlockHeight
}
// If this is the last response we'll send, we'll point
// the last block to the last block of the query.
// Otherwise, we'll set it to the height of the last
// channel in the chunk.
if isFinalChunk {
lastBlockHeight = endBlock
} else {
lastBlockHeight = channelChunk[len(channelChunk)-1].BlockHeight
}
}
// The number of blocks contained in this response (the total

@ -783,52 +783,55 @@ 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
// We'll determine the correct values of each field in
// each response based on the order that they were sent.
var (
expectedFirstBlockHeight uint32
expectedNumBlocks uint32
expectedComplete uint8
)
switch {
// If this is not the last chunk, then Complete should
// be set to zero. Otherwise, it should be one.
case i < 2 && rangeResp.Complete != 0:
t.Fatalf("non-final chunk should have "+
"Complete=0: %v", spew.Sdump(rangeResp))
// The first reply should range from our starting block
// height until it reaches its maximum capacity of
// channels.
case i == 0:
expectedFirstBlockHeight = startingBlockHeight
expectedNumBlocks = chunkSize + 1
case i < 2 && rangeResp.NumBlocks != chunkSize+1:
t.Fatalf("NumBlocks fields in resp "+
"incorrect: expected %v got %v",
chunkSize+1, rangeResp.NumBlocks)
// The last reply should range starting from the next
// block of our previous reply up until the ending
// height of the query. It should also have the Complete
// bit set.
case i == numExpectedChunks-1:
expectedFirstBlockHeight = respMsgs[len(respMsgs)-1].BlockHeight
expectedNumBlocks = endingBlockHeight - expectedFirstBlockHeight + 1
expectedComplete = 1
case i < 2 && rangeResp.FirstBlockHeight !=
uint32(expectedFirstBlockHeight):
// Any intermediate replies should range starting from
// the next block of our previous reply up until it
// reaches its maximum capacity of channels.
default:
expectedFirstBlockHeight = respMsgs[len(respMsgs)-1].BlockHeight
expectedNumBlocks = 5
}
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))
switch {
case rangeResp.FirstBlockHeight != expectedFirstBlockHeight:
t.Fatalf("FirstBlockHeight in resp #%d "+
"incorrect: expected %v, got %v", i+1,
expectedFirstBlockHeight,
rangeResp.FirstBlockHeight)
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)
case rangeResp.NumBlocks != expectedNumBlocks:
t.Fatalf("NumBlocks in resp #%d incorrect: "+
"expected %v, got %v", i+1,
expectedNumBlocks, rangeResp.NumBlocks)
case rangeResp.Complete != expectedComplete:
t.Fatalf("Complete in resp #%d incorrect: "+
"expected %v, got %v", i+1,
expectedNumBlocks, rangeResp.Complete)
}
respMsgs = append(respMsgs, rangeResp.ShortChanIDs...)