Rework and correct getheaders handling.

This commit reworks the getheaders handling a bit to clean it up and match
the reference implementation handling.  In particular, in addition to the
normal handling where headers starting after the block locator up to the
stop hash are served, when no locator hashes are provided, the stop hash
acts as a way to specifically request that header.  Next, an empty headers
message is sent when no hashes provided by the block locator can be
found.  Finally, there was a bug that was limiting the number of headers
that could requested at once to 500 instead of the expected 2000.
This commit is contained in:
Dave Collins 2013-09-04 18:17:42 -05:00
parent cbfee93b74
commit f07d427837

92
peer.go
View file

@ -468,27 +468,35 @@ func (p *peer) handleGetBlocksMsg(msg *btcwire.MsgGetBlocks) {
// handleGetBlocksMsg is invoked when a peer receives a getheaders bitcoin // handleGetBlocksMsg is invoked when a peer receives a getheaders bitcoin
// message. // message.
func (p *peer) handleGetHeadersMsg(msg *btcwire.MsgGetHeaders) { func (p *peer) handleGetHeadersMsg(msg *btcwire.MsgGetHeaders) {
var err error // Attempt to look up the height of the provided stop hash.
startIdx := int64(0)
endIdx := btcdb.AllShas endIdx := btcdb.AllShas
// Return all block hashes to the latest one (up to max per message) if
// no stop hash was specified.
// Attempt to find the ending index of the stop hash if specified.
if !msg.HashStop.IsEqual(&zeroHash) {
block, err := p.server.db.FetchBlockBySha(&msg.HashStop) block, err := p.server.db.FetchBlockBySha(&msg.HashStop)
if err != nil { if err == nil {
// Fetch all if we dont recognize the stop hash. endIdx = block.Height() + 1
endIdx = btcdb.AllShas
}
endIdx = block.Height()
} }
// TODO(davec): This should have some logic to utilize the additional // There are no block locators so a specific header is being requested
// locator hashes to ensure the proper chain. // as identified by the stop hash.
if len(msg.BlockLocatorHashes) == 0 {
// No blocks with the stop hash were found so there is nothing
// to do. Just return. This behavior mirrors the reference
// implementation.
if endIdx == btcdb.AllShas {
return
}
// Send the requested block header.
headersMsg := btcwire.NewMsgHeaders()
hdr := block.MsgBlock().Header // copy
hdr.TxnCount = 0
headersMsg.AddBlockHeader(&hdr)
p.QueueMessage(headersMsg)
return
}
// Find the most recent known block based on the block locator.
startIdx := int64(-1)
for _, hash := range msg.BlockLocatorHashes { for _, hash := range msg.BlockLocatorHashes {
// TODO(drahn) does using the caching interface make sense
// on index lookups ?
block, err := p.server.db.FetchBlockBySha(hash) block, err := p.server.db.FetchBlockBySha(hash)
if err == nil { if err == nil {
// Start with the next hash since we know this one. // Start with the next hash since we know this one.
@ -497,34 +505,58 @@ func (p *peer) handleGetHeadersMsg(msg *btcwire.MsgGetHeaders) {
} }
} }
// When the block locator refers to an unknown block, just return an
// empty headers message. This behavior mirrors the reference
// implementation.
if startIdx == -1 {
headersMsg := btcwire.NewMsgHeaders()
p.QueueMessage(headersMsg)
return
}
// Don't attempt to fetch more than we can put into a single message. // Don't attempt to fetch more than we can put into a single message.
if endIdx-startIdx > btcwire.MaxBlockHeadersPerMsg { if endIdx-startIdx > btcwire.MaxBlockHeadersPerMsg {
endIdx = startIdx + btcwire.MaxBlockHeadersPerMsg endIdx = startIdx + btcwire.MaxBlockHeadersPerMsg
} }
// Fetch the inventory from the block database. // Generate headers message and send it.
hashList, err := p.server.db.FetchHeightRange(startIdx, endIdx) //
if err != nil { // The FetchBlockBySha call is limited to a maximum number of hashes
log.Warnf("lookup returned %v ", err) // per invocation. Since the maximum number of headers per message
return // might be larger, call it multiple times with the appropriate indices
} // as needed.
// Nothing to send.
if len(hashList) == 0 {
return
}
// Generate inventory vectors and push the inventory message.
headersMsg := btcwire.NewMsgHeaders() headersMsg := btcwire.NewMsgHeaders()
for start := startIdx; start < endIdx; {
// Fetch the inventory from the block database.
hashList, err := p.server.db.FetchHeightRange(start, endIdx)
if err != nil {
log.Warnf("[PEER] Header lookup failed: %v", err)
return
}
// The database did not return any further hashes. Break out of
// the loop now.
if len(hashList) == 0 {
break
}
// Add headers to the message.
for _, hash := range hashList { for _, hash := range hashList {
block, err := p.server.db.FetchBlockBySha(&hash) block, err := p.server.db.FetchBlockBySha(&hash)
if err != nil { if err != nil {
log.Warnf("[PEER] badness %v", err) log.Warnf("[PEER] Lookup of known block hash "+
"failed: %v", err)
continue
} }
hdr := block.MsgBlock().Header // copy hdr := block.MsgBlock().Header // copy
hdr.TxnCount = 0 hdr.TxnCount = 0
headersMsg.AddBlockHeader(&hdr) headersMsg.AddBlockHeader(&hdr)
} }
// Start at the next block header after the latest one on the
// next loop iteration.
start += int64(len(hashList))
}
p.QueueMessage(headersMsg) p.QueueMessage(headersMsg)
} }