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

104
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
// message.
func (p *peer) handleGetHeadersMsg(msg *btcwire.MsgGetHeaders) {
var err error
startIdx := int64(0)
// Attempt to look up the height of the provided stop hash.
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)
if err != nil {
// Fetch all if we dont recognize the stop hash.
endIdx = btcdb.AllShas
}
endIdx = block.Height()
block, err := p.server.db.FetchBlockBySha(&msg.HashStop)
if err == nil {
endIdx = block.Height() + 1
}
// TODO(davec): This should have some logic to utilize the additional
// locator hashes to ensure the proper chain.
// There are no block locators so a specific header is being requested
// 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 {
// TODO(drahn) does using the caching interface make sense
// on index lookups ?
block, err := p.server.db.FetchBlockBySha(hash)
if err == nil {
// Start with the next hash since we know this one.
@ -497,33 +505,57 @@ 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.
if endIdx-startIdx > btcwire.MaxBlockHeadersPerMsg {
endIdx = startIdx + btcwire.MaxBlockHeadersPerMsg
}
// Fetch the inventory from the block database.
hashList, err := p.server.db.FetchHeightRange(startIdx, endIdx)
if err != nil {
log.Warnf("lookup returned %v ", err)
return
}
// Nothing to send.
if len(hashList) == 0 {
return
}
// Generate inventory vectors and push the inventory message.
// Generate headers message and send it.
//
// The FetchBlockBySha call is limited to a maximum number of hashes
// per invocation. Since the maximum number of headers per message
// might be larger, call it multiple times with the appropriate indices
// as needed.
headersMsg := btcwire.NewMsgHeaders()
for _, hash := range hashList {
block, err := p.server.db.FetchBlockBySha(&hash)
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] badness %v", err)
log.Warnf("[PEER] Header lookup failed: %v", err)
return
}
hdr := block.MsgBlock().Header // copy
hdr.TxnCount = 0
headersMsg.AddBlockHeader(&hdr)
// 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 {
block, err := p.server.db.FetchBlockBySha(&hash)
if err != nil {
log.Warnf("[PEER] Lookup of known block hash "+
"failed: %v", err)
continue
}
hdr := block.MsgBlock().Header // copy
hdr.TxnCount = 0
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)
}