Filter duplicate getblocks requests.

This commit adds detection and filtering for back-to-back duplicate
getblocks requests.  This is needed because the trigger for requesting
more blocks is receiving an orphan.  When the peer is further behind than
the number of blocks advertised via a single inventory message, the same
orphan block will be sent multiple times.  When the peer receives the
final inventory message, it too contains the orphan that was previously
sent.  This leads to a duplicate getblocks request that must be filtered
to prevent requesting the final series of blocks again.
This commit is contained in:
Dave Collins 2013-09-18 13:33:54 -05:00
parent 0195306ff7
commit 346ff6f9e2

82
peer.go
View file

@ -91,31 +91,34 @@ func newNetAddress(addr net.Addr, services btcwire.ServiceFlag) (*btcwire.NetAdd
// peer provides a bitcoin peer for handling bitcoin communications. // peer provides a bitcoin peer for handling bitcoin communications.
type peer struct { type peer struct {
server *server server *server
protocolVersion uint32 protocolVersion uint32
btcnet btcwire.BitcoinNet btcnet btcwire.BitcoinNet
services btcwire.ServiceFlag services btcwire.ServiceFlag
started bool started bool
conn net.Conn conn net.Conn
addr string addr string
na *btcwire.NetAddress na *btcwire.NetAddress
timeConnected time.Time timeConnected time.Time
inbound bool inbound bool
disconnect bool disconnect bool
persistent bool persistent bool
versionKnown bool versionKnown bool
knownAddresses map[string]bool knownAddresses map[string]bool
knownInventory *MruInventoryMap knownInventory *MruInventoryMap
knownInvMutex sync.Mutex knownInvMutex sync.Mutex
lastBlock int32 lastBlock int32
requestQueue *list.List prevGetBlocksBegin *btcwire.ShaHash
invSendQueue *list.List prevGetBlocksStop *btcwire.ShaHash
continueHash *btcwire.ShaHash prevGetBlockMutex sync.Mutex
wg sync.WaitGroup requestQueue *list.List
outputQueue chan btcwire.Message invSendQueue *list.List
outputInvChan chan *btcwire.InvVect continueHash *btcwire.ShaHash
blockProcessed chan bool wg sync.WaitGroup
quit chan bool outputQueue chan btcwire.Message
outputInvChan chan *btcwire.InvVect
blockProcessed chan bool
quit chan bool
} }
// isKnownInventory returns whether or not the peer is known to have the passed // isKnownInventory returns whether or not the peer is known to have the passed
@ -340,8 +343,30 @@ func (p *peer) pushBlockMsg(sha btcwire.ShaHash) error {
} }
// pushGetBlocksMsg sends a getblocks message for the provided block locator // pushGetBlocksMsg sends a getblocks message for the provided block locator
// and stop hash. // and stop hash. It will ignore back-to-back duplicate requests.
func (p *peer) pushGetBlocksMsg(locator btcchain.BlockLocator, stopHash *btcwire.ShaHash) error { func (p *peer) pushGetBlocksMsg(locator btcchain.BlockLocator, stopHash *btcwire.ShaHash) error {
p.prevGetBlockMutex.Lock()
defer p.prevGetBlockMutex.Unlock()
// Extract the begin hash from the block locator, if one was specified,
// to use for filtering duplicate getblocks requests.
// request.
var beginHash *btcwire.ShaHash
if len(locator) > 0 {
beginHash = locator[0]
}
// Filter duplicate getblocks requests.
if p.prevGetBlocksStop != nil && p.prevGetBlocksBegin != nil &&
beginHash != nil && stopHash.IsEqual(p.prevGetBlocksStop) &&
beginHash.IsEqual(p.prevGetBlocksBegin) {
log.Tracef("[PEER] Filtering duplicate [getblocks] with begin "+
"hash %v, stop hash %v", beginHash, stopHash)
return nil
}
// Construct the getblocks request and queue it to be sent.
msg := btcwire.NewMsgGetBlocks(stopHash) msg := btcwire.NewMsgGetBlocks(stopHash)
for _, hash := range locator { for _, hash := range locator {
err := msg.AddBlockLocatorHash(hash) err := msg.AddBlockLocatorHash(hash)
@ -350,6 +375,11 @@ func (p *peer) pushGetBlocksMsg(locator btcchain.BlockLocator, stopHash *btcwire
} }
} }
p.QueueMessage(msg) p.QueueMessage(msg)
// Update the previous getblocks request information for filtering
// duplicates.
p.prevGetBlocksBegin = beginHash
p.prevGetBlocksStop = stopHash
return nil return nil
} }