Add message blocking semantics to block processing.
This commit modifies the input message handler so that when a remote peer sends a block, no further messages from that peer are accepted until the block has been fully processed and therefore 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 block has been fully processed as well.
This commit is contained in:
parent
2570ecd2ac
commit
60779ea8df
2 changed files with 23 additions and 10 deletions
|
@ -31,6 +31,7 @@ type inventoryItem struct {
|
||||||
// so the block handler has access to that information.
|
// so the block handler has access to that information.
|
||||||
type blockMsg struct {
|
type blockMsg struct {
|
||||||
block *btcutil.Block
|
block *btcutil.Block
|
||||||
|
peer *peer
|
||||||
}
|
}
|
||||||
|
|
||||||
// invMsg packages a bitcoin inv message and the peer it came from together
|
// invMsg packages a bitcoin inv message and the peer it came from together
|
||||||
|
@ -209,8 +210,9 @@ out:
|
||||||
for !b.shutdown {
|
for !b.shutdown {
|
||||||
select {
|
select {
|
||||||
// Handle new block messages.
|
// Handle new block messages.
|
||||||
case msg := <-b.blockQueue:
|
case bmsg := <-b.blockQueue:
|
||||||
b.handleBlockMsg(msg.block)
|
b.handleBlockMsg(bmsg.block)
|
||||||
|
bmsg.peer.blockProcessed <- true
|
||||||
|
|
||||||
// Handle new inventory messages.
|
// Handle new inventory messages.
|
||||||
case msg := <-b.invQueue:
|
case msg := <-b.invQueue:
|
||||||
|
@ -287,13 +289,14 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueueBlock adds the passed block message and peer to the block handling queue.
|
// 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.
|
// Don't accept more blocks if we're shutting down.
|
||||||
if b.shutdown {
|
if b.shutdown {
|
||||||
|
p.blockProcessed <- false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bmsg := blockMsg{block: block}
|
bmsg := blockMsg{block: block, peer: p}
|
||||||
b.blockQueue <- &bmsg
|
b.blockQueue <- &bmsg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
peer.go
22
peer.go
|
@ -93,6 +93,7 @@ type peer struct {
|
||||||
lastBlock int32
|
lastBlock int32
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
outputQueue chan btcwire.Message
|
outputQueue chan btcwire.Message
|
||||||
|
blockProcessed chan bool
|
||||||
quit chan bool
|
quit chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,11 +634,7 @@ out:
|
||||||
break out
|
break out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some messages are handled directly, while other messages
|
// Handle each supported message type.
|
||||||
// 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.
|
|
||||||
switch msg := rmsg.(type) {
|
switch msg := rmsg.(type) {
|
||||||
case *btcwire.MsgVersion:
|
case *btcwire.MsgVersion:
|
||||||
p.handleVersionMsg(msg)
|
p.handleVersionMsg(msg)
|
||||||
|
@ -662,8 +659,20 @@ out:
|
||||||
p.server.BroadcastMessage(msg, p)
|
p.server.BroadcastMessage(msg, p)
|
||||||
|
|
||||||
case *btcwire.MsgBlock:
|
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)
|
block := btcutil.NewBlockFromBlockAndBytes(msg, buf)
|
||||||
p.server.blockManager.QueueBlock(block)
|
p.server.blockManager.QueueBlock(block, p)
|
||||||
|
<-p.blockProcessed
|
||||||
|
|
||||||
case *btcwire.MsgInv:
|
case *btcwire.MsgInv:
|
||||||
p.server.blockManager.QueueInv(msg, p)
|
p.server.blockManager.QueueInv(msg, p)
|
||||||
|
@ -783,6 +792,7 @@ func newPeer(s *server, conn net.Conn, inbound bool, persistent bool) *peer {
|
||||||
persistent: persistent,
|
persistent: persistent,
|
||||||
knownAddresses: make(map[string]bool),
|
knownAddresses: make(map[string]bool),
|
||||||
outputQueue: make(chan btcwire.Message, outputBufferSize),
|
outputQueue: make(chan btcwire.Message, outputBufferSize),
|
||||||
|
blockProcessed: make(chan bool, 1),
|
||||||
quit: make(chan bool),
|
quit: make(chan bool),
|
||||||
}
|
}
|
||||||
return &p
|
return &p
|
||||||
|
|
Loading…
Reference in a new issue