mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-10 16:16:47 +00:00
86 lines
2.8 KiB
Go
86 lines
2.8 KiB
Go
package p2p
|
|
|
|
import (
|
|
"github.com/kaspanet/kaspad/peer"
|
|
"github.com/kaspanet/kaspad/util/daghash"
|
|
"github.com/kaspanet/kaspad/wire"
|
|
)
|
|
|
|
// OnGetData is invoked when a peer receives a getdata kaspa message and
|
|
// is used to deliver block and transaction information.
|
|
func (sp *Peer) OnGetData(_ *peer.Peer, msg *wire.MsgGetData) {
|
|
numAdded := 0
|
|
notFound := wire.NewMsgNotFound()
|
|
|
|
length := len(msg.InvList)
|
|
// A decaying ban score increase is applied to prevent exhausting resources
|
|
// with unusually large inventory queries.
|
|
// Requesting more than the maximum inventory vector length within a short
|
|
// period of time yields a score above the default ban threshold. Sustained
|
|
// bursts of small requests are not penalized as that would potentially ban
|
|
// peers performing IBD.
|
|
// This incremental score decays each minute to half of its value.
|
|
sp.addBanScore(0, uint32(length)*99/wire.MaxInvPerMsg, "getdata")
|
|
|
|
// We wait on this wait channel periodically to prevent queuing
|
|
// far more data than we can send in a reasonable time, wasting memory.
|
|
// The waiting occurs after the database fetch for the next one to
|
|
// provide a little pipelining.
|
|
var waitChan chan struct{}
|
|
doneChan := make(chan struct{}, 1)
|
|
|
|
for i, iv := range msg.InvList {
|
|
var c chan struct{}
|
|
// If this will be the last message we send.
|
|
if i == length-1 && len(notFound.InvList) == 0 {
|
|
c = doneChan
|
|
} else if (i+1)%3 == 0 {
|
|
// Buffered so as to not make the send goroutine block.
|
|
c = make(chan struct{}, 1)
|
|
}
|
|
var err error
|
|
switch iv.Type {
|
|
case wire.InvTypeTx:
|
|
err = sp.server.pushTxMsg(sp, (*daghash.TxID)(iv.Hash), c, waitChan)
|
|
case wire.InvTypeSyncBlock:
|
|
fallthrough
|
|
case wire.InvTypeMissingAncestor:
|
|
fallthrough
|
|
case wire.InvTypeBlock:
|
|
err = sp.server.pushBlockMsg(sp, iv.Hash, c, waitChan)
|
|
case wire.InvTypeFilteredBlock:
|
|
err = sp.server.pushMerkleBlockMsg(sp, iv.Hash, c, waitChan)
|
|
default:
|
|
peerLog.Warnf("Unknown type in inventory request %d",
|
|
iv.Type)
|
|
continue
|
|
}
|
|
if err != nil {
|
|
notFound.AddInvVect(iv)
|
|
|
|
// When there is a failure fetching the final entry
|
|
// and the done channel was sent in due to there
|
|
// being no outstanding not found inventory, consume
|
|
// it here because there is now not found inventory
|
|
// that will use the channel momentarily.
|
|
if i == len(msg.InvList)-1 && c != nil {
|
|
<-c
|
|
}
|
|
}
|
|
numAdded++
|
|
waitChan = c
|
|
}
|
|
if len(notFound.InvList) != 0 {
|
|
sp.QueueMessage(notFound, doneChan)
|
|
}
|
|
|
|
// Wait for messages to be sent. We can send quite a lot of data at this
|
|
// point and this will keep the peer busy for a decent amount of time.
|
|
// We don't process anything else by them in this time so that we
|
|
// have an idea of when we should hear back from them - else the idle
|
|
// timeout could fire when we were only half done sending the blocks.
|
|
if numAdded > 0 {
|
|
<-doneChan
|
|
}
|
|
}
|