diff --git a/blockmanager.go b/blockmanager.go index b7330ae82..94db66993 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -31,6 +31,7 @@ type inventoryItem struct { // so the block handler has access to that information. type blockMsg struct { block *btcutil.Block + peer *peer } // invMsg packages a bitcoin inv message and the peer it came from together @@ -209,8 +210,9 @@ out: for !b.shutdown { select { // Handle new block messages. - case msg := <-b.blockQueue: - b.handleBlockMsg(msg.block) + case bmsg := <-b.blockQueue: + b.handleBlockMsg(bmsg.block) + bmsg.peer.blockProcessed <- true // Handle new inventory messages. case msg := <-b.invQueue: @@ -287,13 +289,14 @@ out: } // QueueBlock adds the passed block message and peer to the block handling queue. -func (b *blockManager) QueueBlock(block *btcutil.Block) { +func (b *blockManager) QueueBlock(block *btcutil.Block, p *peer) { // Don't accept more blocks if we're shutting down. if b.shutdown { + p.blockProcessed <- false return } - bmsg := blockMsg{block: block} + bmsg := blockMsg{block: block, peer: p} b.blockQueue <- &bmsg } diff --git a/peer.go b/peer.go index 47f521cfd..437bdf499 100644 --- a/peer.go +++ b/peer.go @@ -93,6 +93,7 @@ type peer struct { lastBlock int32 wg sync.WaitGroup outputQueue chan btcwire.Message + blockProcessed chan bool quit chan bool } @@ -633,11 +634,7 @@ out: break out } - // Some messages are handled directly, while other messages - // are sent to a queue to be processed. Directly handling - // getdata and getblocks messages makes it impossible for a peer - // to spam with requests. However, it means that our getdata - // requests to it may not get prompt replies. + // Handle each supported message type. switch msg := rmsg.(type) { case *btcwire.MsgVersion: p.handleVersionMsg(msg) @@ -662,8 +659,20 @@ out: p.server.BroadcastMessage(msg, p) case *btcwire.MsgBlock: + // Queue the block up to be handled by the block + // manager and intentionally block further receives + // until the bitcoin block is fully processed and known + // good or bad. This helps prevent a malicious peer + // from queueing up a bunch of bad blocks before + // disconnecting (or being disconnected) and wasting + // memory. Additionally, this behavior is depended on + // by at least the block acceptance test tool as the + // reference implementation processes blocks in the same + // thread and therefore blocks further messages until + // the bitcoin block has been fully processed. block := btcutil.NewBlockFromBlockAndBytes(msg, buf) - p.server.blockManager.QueueBlock(block) + p.server.blockManager.QueueBlock(block, p) + <-p.blockProcessed case *btcwire.MsgInv: p.server.blockManager.QueueInv(msg, p) @@ -783,6 +792,7 @@ func newPeer(s *server, conn net.Conn, inbound bool, persistent bool) *peer { persistent: persistent, knownAddresses: make(map[string]bool), outputQueue: make(chan btcwire.Message, outputBufferSize), + blockProcessed: make(chan bool, 1), quit: make(chan bool), } return &p