forked from TrueCloudLab/neoneo-go
abb4da9cbd
[peermgr] - Add request cache with tests - Add requestCache to peermgr - refactored peer manager tests - Added blockInfo struct, to allow sorting on the blockIndex - added helper methods for cache, pickItem, pickFirstItem, removeHash, findHash and refactored tests - renamed requestcache to blockcache - refactored peer manager to use block cache for block requests *only* - added blockCallPeer function to handle block requests only - refactored onDisconnect to add back any pending peer requests that the disconnected peer did not complete into the peer manager queue [peermgr/server] - Modify onBlock handler in server, to send peermgr a BlockInfo struct [peermgr/syncmgr/server] - Modified blockIndex in BlockInfo to be uint32 and not uint64 - RequestBlocks in syncmgr now takes an index along with the hash - modified syncmgr code to pass index along with hash in all methods
201 lines
4.8 KiB
Go
201 lines
4.8 KiB
Go
package peermgr
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/CityOfZion/neo-go/pkg/wire/command"
|
|
"github.com/CityOfZion/neo-go/pkg/wire/util"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
type peer struct {
|
|
quit chan bool
|
|
nonce int
|
|
disconnected bool
|
|
blockRequested int
|
|
headersRequested int
|
|
}
|
|
|
|
func (p *peer) Disconnect() {
|
|
p.disconnected = true
|
|
p.quit <- true
|
|
}
|
|
func (p *peer) RequestBlocks([]util.Uint256) error {
|
|
p.blockRequested++
|
|
return nil
|
|
}
|
|
func (p *peer) RequestHeaders(util.Uint256) error {
|
|
p.headersRequested++
|
|
return nil
|
|
}
|
|
func (p *peer) NotifyDisconnect() {
|
|
<-p.quit
|
|
}
|
|
|
|
func TestAddPeer(t *testing.T) {
|
|
pmgr := New()
|
|
|
|
peerA := &peer{nonce: 1}
|
|
peerB := &peer{nonce: 2}
|
|
peerC := &peer{nonce: 3}
|
|
|
|
pmgr.AddPeer(peerA)
|
|
pmgr.AddPeer(peerB)
|
|
pmgr.AddPeer(peerC)
|
|
pmgr.AddPeer(peerC)
|
|
|
|
assert.Equal(t, 3, pmgr.Len())
|
|
}
|
|
|
|
func TestRequestBlocks(t *testing.T) {
|
|
pmgr := New()
|
|
|
|
peerA := &peer{nonce: 1}
|
|
peerB := &peer{nonce: 2}
|
|
peerC := &peer{nonce: 3}
|
|
|
|
pmgr.AddPeer(peerA)
|
|
pmgr.AddPeer(peerB)
|
|
pmgr.AddPeer(peerC)
|
|
|
|
firstBlock := randomBlockInfo(t)
|
|
err := pmgr.RequestBlock(firstBlock)
|
|
assert.Nil(t, err)
|
|
|
|
secondBlock := randomBlockInfo(t)
|
|
err = pmgr.RequestBlock(secondBlock)
|
|
assert.Nil(t, err)
|
|
|
|
thirdBlock := randomBlockInfo(t)
|
|
err = pmgr.RequestBlock(thirdBlock)
|
|
assert.Nil(t, err)
|
|
|
|
// Since the peer manager did not get a MsgReceived
|
|
// in between the block requests
|
|
// a request should be sent to all peers
|
|
// This is only true, if peerBlockCacheLimit == 1
|
|
|
|
assert.Equal(t, 1, peerA.blockRequested)
|
|
assert.Equal(t, 1, peerB.blockRequested)
|
|
assert.Equal(t, 1, peerC.blockRequested)
|
|
|
|
// Since the peer manager still has not received a MsgReceived
|
|
// another call to request blocks, will add the request to the cache
|
|
// and return a nil err
|
|
|
|
fourthBlock := randomBlockInfo(t)
|
|
err = pmgr.RequestBlock(fourthBlock)
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, 1, pmgr.requestCache.cacheLen())
|
|
|
|
// If we tell the peer manager that we have received a block
|
|
// it will check the cache for any pending requests and send a block request if there are any.
|
|
// The request will go to the peer who sent back the block corresponding to the first hash
|
|
// since the other two peers are still busy with their block requests
|
|
|
|
peer := findPeerwithHash(t, pmgr, firstBlock.BlockHash)
|
|
err = pmgr.BlockMsgReceived(peer, firstBlock)
|
|
assert.Nil(t, err)
|
|
|
|
totalRequests := peerA.blockRequested + peerB.blockRequested + peerC.blockRequested
|
|
assert.Equal(t, 4, totalRequests)
|
|
|
|
// // cache should be empty now
|
|
assert.Equal(t, 0, pmgr.requestCache.cacheLen())
|
|
}
|
|
|
|
// The peer manager does not tell you what peer was sent a particular block request
|
|
// For testing purposes, the following function will find that peer
|
|
func findPeerwithHash(t *testing.T, pmgr *PeerMgr, blockHash util.Uint256) mPeer {
|
|
for peer, stats := range pmgr.peers {
|
|
_, err := stats.blockCache.findHash(blockHash)
|
|
if err == nil {
|
|
return peer
|
|
}
|
|
}
|
|
assert.Fail(t, "cannot find a peer with that hash")
|
|
return nil
|
|
}
|
|
|
|
func TestRequestHeaders(t *testing.T) {
|
|
pmgr := New()
|
|
|
|
peerA := &peer{nonce: 1}
|
|
peerB := &peer{nonce: 2}
|
|
peerC := &peer{nonce: 3}
|
|
|
|
pmgr.AddPeer(peerA)
|
|
pmgr.AddPeer(peerB)
|
|
pmgr.AddPeer(peerC)
|
|
|
|
err := pmgr.RequestHeaders(util.Uint256{})
|
|
assert.Nil(t, err)
|
|
|
|
err = pmgr.RequestHeaders(util.Uint256{})
|
|
assert.Nil(t, err)
|
|
|
|
err = pmgr.RequestHeaders(util.Uint256{})
|
|
assert.Nil(t, err)
|
|
|
|
// Since the peer manager did not get a MsgReceived
|
|
// in between the header requests
|
|
// a request should be sent to all peers
|
|
|
|
assert.Equal(t, 1, peerA.headersRequested)
|
|
assert.Equal(t, 1, peerB.headersRequested)
|
|
assert.Equal(t, 1, peerC.headersRequested)
|
|
|
|
// Since the peer manager still has not received a MsgReceived
|
|
// another call to request header, will return a NoAvailablePeerError
|
|
|
|
err = pmgr.RequestHeaders(util.Uint256{})
|
|
assert.Equal(t, ErrNoAvailablePeers, err)
|
|
|
|
// If we tell the peer manager that peerA has given us a block
|
|
// then send another BlockRequest. It will go to peerA
|
|
// since the other two peers are still busy with their
|
|
// block requests
|
|
|
|
err = pmgr.MsgReceived(peerA, command.Headers)
|
|
assert.Nil(t, err)
|
|
err = pmgr.RequestHeaders(util.Uint256{})
|
|
assert.Nil(t, err)
|
|
|
|
assert.Equal(t, 2, peerA.headersRequested)
|
|
assert.Equal(t, 1, peerB.headersRequested)
|
|
assert.Equal(t, 1, peerC.headersRequested)
|
|
}
|
|
|
|
func TestUnknownPeer(t *testing.T) {
|
|
pmgr := New()
|
|
|
|
unknownPeer := &peer{
|
|
disconnected: false,
|
|
quit: make(chan bool),
|
|
}
|
|
|
|
err := pmgr.MsgReceived(unknownPeer, command.Headers)
|
|
assert.Equal(t, true, unknownPeer.disconnected)
|
|
assert.Equal(t, ErrUnknownPeer, err)
|
|
}
|
|
|
|
func TestNotifyDisconnect(t *testing.T) {
|
|
pmgr := New()
|
|
|
|
peerA := &peer{
|
|
nonce: 1,
|
|
quit: make(chan bool),
|
|
}
|
|
|
|
pmgr.AddPeer(peerA)
|
|
|
|
if pmgr.Len() != 1 {
|
|
t.Fail()
|
|
}
|
|
|
|
peerA.Disconnect()
|
|
|
|
if pmgr.Len() != 0 {
|
|
t.Fail()
|
|
}
|
|
}
|