Skip to content

Commit

Permalink
[release-v1.6] server: Only send fast block anns to full nodes.
Browse files Browse the repository at this point in the history
Fast block announcements were added a few years ago as a way to increase
the propagation speed of new blocks that extend the main chain through
the network to significantly reduce voting latency as well as how
quickly proof-of-work miners discover new blocks.  This is desirable
behavior, however, it should only apply to full nodes since only they
are equipped to deal with the fast announcements.

As a case in point, header commitments and committed block filters can
pose a problem for lightweight clients that rely on them in the case of
fast block announcements because lightweight clients tend to request the
filters as soon as they learn about the new blocks and those filters
have not been created yet when fast block announcements are sent.

Since there is network latency involved in the request process, the
result is that sometimes the filters are available by the time the
requests from lightweight clients are received and sometimes they are
not.

In order to help prevent the aforementioned case, and other similar
ones, this modifies the code that deals with block announcements to only
send fast block announcements to peers that advertise themselves as full
nodes instead of all nodes and separates out the block announcement
logic slightly to make future enhancements easier in the process.

It also modifies the filtering of duplicate block announcements to work
on a per peer basis since not all peers receive the fast block
announcement now.
  • Loading branch information
davecgh committed Mar 1, 2021
1 parent a7269e3 commit 9ca673a
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 30 deletions.
31 changes: 11 additions & 20 deletions blockmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ type peerNotifier interface {
// that are not already known to have it.
RelayInventory(invVect *wire.InvVect, data interface{}, immediate bool)

// RelayBlockAnnouncement creates a block announcement for the passed block
// and relays that announcement to all connected peers that advertise the
// given required services and are not already known to have it.
RelayBlockAnnouncement(block *dcrutil.Block, reqServices wire.ServiceFlag)

// TransactionConfirmed marks the provided single confirmation transaction
// as no longer needing rebroadcasting.
TransactionConfirmed(tx *dcrutil.Tx)
Expand Down Expand Up @@ -337,10 +342,6 @@ type blockManager struct {
lotteryDataBroadcast map[chainhash.Hash]struct{}
lotteryDataBroadcastMutex sync.RWMutex

// The following fields are used to filter duplicate block announcements.
announcedBlockMtx sync.Mutex
announcedBlock *chainhash.Hash

// The following fields are used to track the height being synced to from
// peers.
syncHeightMtx sync.Mutex
Expand Down Expand Up @@ -1863,12 +1864,8 @@ func (b *blockManager) handleBlockchainNotification(notification *blockchain.Not
break
}

// Generate the inventory vector and relay it immediately.
iv := wire.NewInvVect(wire.InvTypeBlock, block.Hash())
b.cfg.PeerNotifier.RelayInventory(iv, block.MsgBlock().Header, true)
b.announcedBlockMtx.Lock()
b.announcedBlock = block.Hash()
b.announcedBlockMtx.Unlock()
// Relay the block announcement immediately to full nodes.
b.cfg.PeerNotifier.RelayBlockAnnouncement(block, wire.SFNodeNetwork)

// A block has been accepted into the block chain. Relay it to other peers
// (will be ignored if already relayed via NTNewTipBlockChecked) and
Expand Down Expand Up @@ -1957,16 +1954,10 @@ func (b *blockManager) handleBlockchainNotification(notification *blockchain.Not
}
}

// Generate the inventory vector and relay it immediately if not already
// known to have been sent in NTNewTipBlockChecked.
b.announcedBlockMtx.Lock()
sent := b.announcedBlock != nil && *b.announcedBlock == *blockHash
b.announcedBlock = nil
b.announcedBlockMtx.Unlock()
if !sent {
iv := wire.NewInvVect(wire.InvTypeBlock, blockHash)
b.cfg.PeerNotifier.RelayInventory(iv, block.MsgBlock().Header, true)
}
// Relay the block announcement immediately to all peers that were not
// already notified via NTNewTipBlockChecked.
const noRequiredServices = 0
b.cfg.PeerNotifier.RelayBlockAnnouncement(block, noRequiredServices)

// Inform the background block template generator about the accepted
// block.
Expand Down
53 changes: 43 additions & 10 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,10 @@ type broadcastPruneInventory struct{}
// (it will be put into a trickle queue if false) so the relay has access to
// that information.
type relayMsg struct {
invVect *wire.InvVect
data interface{}
immediate bool
invVect *wire.InvVect
data interface{}
immediate bool
reqServices wire.ServiceFlag
}

// updatePeerHeightsMsg is a message sent from the blockmanager to the server
Expand Down Expand Up @@ -508,6 +509,10 @@ type serverPeer struct {
// peerNa is network address of the peer connected to.
peerNa *wire.NetAddress
peerNaMtx sync.Mutex

// announcedBlock tracks the most recent block announced to this peer and is
// used to filter duplicates.
announcedBlock *chainhash.Hash
}

// newServerPeer returns a new serverPeer instance. The peer needs to be set by
Expand Down Expand Up @@ -1952,10 +1957,25 @@ func (s *server) handleRelayInvMsg(state *peerState, msg relayMsg) {
return
}

// If the inventory is a block and the peer prefers headers,
// generate and send a headers message instead of an inventory
// message.
if msg.invVect.Type == wire.InvTypeBlock && sp.WantsHeaders() {
// Ignore peers that do not have the required service flags.
if !hasServices(sp.Services(), msg.reqServices) {
return
}

// Filter duplicate block announcements.
iv := msg.invVect
isBlockAnnouncement := iv.Type == wire.InvTypeBlock
if isBlockAnnouncement {
if sp.announcedBlock != nil && *sp.announcedBlock == iv.Hash {
sp.announcedBlock = nil
return
}
sp.announcedBlock = &iv.Hash
}

// Generate and send a headers message instead of an inventory message
// for block announcements when the peer prefers headers.
if isBlockAnnouncement && sp.WantsHeaders() {
blockHeader, ok := msg.data.(wire.BlockHeader)
if !ok {
peerLog.Warnf("Underlying data for headers" +
Expand All @@ -1972,7 +1992,7 @@ func (s *server) handleRelayInvMsg(state *peerState, msg relayMsg) {
return
}

if msg.invVect.Type == wire.InvTypeTx {
if iv.Type == wire.InvTypeTx {
// Don't relay the transaction to the peer when it has
// transaction relaying disabled.
if sp.relayTxDisabled() {
Expand All @@ -1986,9 +2006,9 @@ func (s *server) handleRelayInvMsg(state *peerState, msg relayMsg) {
// It will be ignored in either case if the peer is already
// known to have the inventory.
if msg.immediate {
sp.QueueInventoryImmediate(msg.invVect)
sp.QueueInventoryImmediate(iv)
} else {
sp.QueueInventory(msg.invVect)
sp.QueueInventory(iv)
}
})
}
Expand Down Expand Up @@ -2434,6 +2454,19 @@ func (s *server) RelayInventory(invVect *wire.InvVect, data interface{}, immedia
s.relayInv <- relayMsg{invVect: invVect, data: data, immediate: immediate}
}

// RelayBlockAnnouncement creates a block announcement for the passed block and
// relays that announcement immediately to all connected peers that advertise
// the given required services and are not already known to have it.
func (s *server) RelayBlockAnnouncement(block *dcrutil.Block, reqServices wire.ServiceFlag) {
invVect := wire.NewInvVect(wire.InvTypeBlock, block.Hash())
s.relayInv <- relayMsg{
invVect: invVect,
data: block.MsgBlock().Header,
immediate: true,
reqServices: reqServices,
}
}

// BroadcastMessage sends msg to all peers currently connected to the server
// except those in the passed peers to exclude.
func (s *server) BroadcastMessage(msg wire.Message, exclPeers ...*serverPeer) {
Expand Down

0 comments on commit 9ca673a

Please sign in to comment.