github.com/koko1123/flow-go-1@v0.29.6/network/test/blob_service_test.go (about)

     1  package test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/ipfs/go-cid"
    11  	"github.com/ipfs/go-datastore"
    12  	"github.com/ipfs/go-datastore/sync"
    13  	blockstore "github.com/ipfs/go-ipfs-blockstore"
    14  	"github.com/rs/zerolog"
    15  	"github.com/stretchr/testify/suite"
    16  	"go.uber.org/atomic"
    17  
    18  	"github.com/koko1123/flow-go-1/network/p2p/dht"
    19  
    20  	"github.com/koko1123/flow-go-1/utils/unittest"
    21  
    22  	"github.com/koko1123/flow-go-1/model/flow"
    23  	"github.com/koko1123/flow-go-1/module/blobs"
    24  	"github.com/koko1123/flow-go-1/module/irrecoverable"
    25  	"github.com/koko1123/flow-go-1/module/util"
    26  	"github.com/koko1123/flow-go-1/network"
    27  	"github.com/koko1123/flow-go-1/network/channels"
    28  	"github.com/koko1123/flow-go-1/network/internal/testutils"
    29  	"github.com/koko1123/flow-go-1/network/mocknetwork"
    30  )
    31  
    32  // conditionalTopology is a topology that behaves like the underlying topology when the condition is true,
    33  // otherwise returns an empty identity list.
    34  type conditionalTopology struct {
    35  	top       network.Topology
    36  	condition func() bool
    37  }
    38  
    39  var _ network.Topology = (*conditionalTopology)(nil)
    40  
    41  func (t *conditionalTopology) Fanout(ids flow.IdentityList) flow.IdentityList {
    42  	if t.condition() {
    43  		return t.top.Fanout(ids)
    44  	} else {
    45  		return flow.IdentityList{}
    46  	}
    47  }
    48  
    49  type BlobServiceTestSuite struct {
    50  	suite.Suite
    51  
    52  	cancel       context.CancelFunc
    53  	networks     []network.Network
    54  	blobServices []network.BlobService
    55  	datastores   []datastore.Batching
    56  	blobCids     []cid.Cid
    57  	numNodes     int
    58  }
    59  
    60  func TestBlobService(t *testing.T) {
    61  	t.Parallel()
    62  	suite.Run(t, new(BlobServiceTestSuite))
    63  }
    64  
    65  func (suite *BlobServiceTestSuite) putBlob(ds datastore.Batching, blob blobs.Blob) {
    66  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    67  	defer cancel()
    68  	suite.Require().NoError(blockstore.NewBlockstore(ds).Put(ctx, blob))
    69  }
    70  
    71  func (suite *BlobServiceTestSuite) SetupTest() {
    72  	suite.numNodes = 3
    73  
    74  	logger := zerolog.New(os.Stdout)
    75  
    76  	// Bitswap listens to connect events but doesn't iterate over existing connections, and fixing this without
    77  	// race conditions is tricky given the way the code is architected. As a result, libP2P hosts must first listen
    78  	// on Bitswap before connecting to each other, otherwise their Bitswap requests may never reach each other.
    79  	// See https://github.com/ipfs/go-bitswap/issues/525 for more details.
    80  	topologyActive := atomic.NewBool(false)
    81  
    82  	ctx, cancel := context.WithCancel(context.Background())
    83  	suite.cancel = cancel
    84  
    85  	signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx)
    86  
    87  	ids, nodes, mws, networks, _ := testutils.GenerateIDsMiddlewaresNetworks(
    88  		suite.T(),
    89  		suite.numNodes,
    90  		logger,
    91  		unittest.NetworkCodec(),
    92  		mocknetwork.NewViolationsConsumer(suite.T()),
    93  		testutils.WithDHT("blob_service_test", dht.AsServer()),
    94  		testutils.WithPeerUpdateInterval(time.Second),
    95  	)
    96  	suite.networks = networks
    97  
    98  	testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, networks, 100*time.Millisecond)
    99  
   100  	blobExchangeChannel := channels.Channel("blob-exchange")
   101  
   102  	for i, net := range networks {
   103  		ds := sync.MutexWrap(datastore.NewMapDatastore())
   104  		suite.datastores = append(suite.datastores, ds)
   105  		blob := blobs.NewBlob([]byte(fmt.Sprintf("foo%v", i)))
   106  		suite.blobCids = append(suite.blobCids, blob.Cid())
   107  		suite.putBlob(ds, blob)
   108  		blobService, err := net.RegisterBlobService(blobExchangeChannel, ds)
   109  		suite.Require().NoError(err)
   110  		<-blobService.Ready()
   111  		suite.blobServices = append(suite.blobServices, blobService)
   112  	}
   113  
   114  	// let nodes connect to each other only after they are all listening on Bitswap
   115  	topologyActive.Store(true)
   116  	suite.Require().Eventually(func() bool {
   117  		for i, mw := range mws {
   118  			for j := i + 1; j < suite.numNodes; j++ {
   119  				connected, err := mw.IsConnected(ids[j].NodeID)
   120  				suite.Require().NoError(err)
   121  				if !connected {
   122  					return false
   123  				}
   124  			}
   125  		}
   126  		return true
   127  	}, 3*time.Second, 100*time.Millisecond)
   128  }
   129  
   130  func (suite *BlobServiceTestSuite) TearDownTest() {
   131  	suite.cancel()
   132  
   133  	netDoneChans := make([]<-chan struct{}, len(suite.networks))
   134  	for i, net := range suite.networks {
   135  		netDoneChans[i] = net.Done()
   136  	}
   137  	<-util.AllClosed(netDoneChans...)
   138  
   139  	suite.networks = nil
   140  	suite.cancel = nil
   141  	suite.blobServices = nil
   142  	suite.datastores = nil
   143  	suite.blobCids = nil
   144  }
   145  
   146  func (suite *BlobServiceTestSuite) TestGetBlobs() {
   147  	for i, bex := range suite.blobServices {
   148  		// check that we can get all other blobs
   149  		var blobsToGet []cid.Cid
   150  		unreceivedBlobs := make(map[cid.Cid]struct{})
   151  		for j, blobCid := range suite.blobCids {
   152  			if j != i {
   153  				blobsToGet = append(blobsToGet, blobCid)
   154  				unreceivedBlobs[blobCid] = struct{}{}
   155  			}
   156  		}
   157  
   158  		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   159  		defer cancel()
   160  
   161  		blobs := bex.GetBlobs(ctx, blobsToGet)
   162  
   163  		for blob := range blobs {
   164  			delete(unreceivedBlobs, blob.Cid())
   165  		}
   166  
   167  		for c := range unreceivedBlobs {
   168  			suite.T().Errorf("Blob %v not received by node %v", c, i)
   169  		}
   170  	}
   171  }
   172  
   173  func (suite *BlobServiceTestSuite) TestGetBlobsWithSession() {
   174  	for i, bex := range suite.blobServices {
   175  		// check that we can get all other blobs in a single session
   176  		blobsToGet := make(map[cid.Cid]struct{})
   177  		for j, blobCid := range suite.blobCids {
   178  			if j != i {
   179  				blobsToGet[blobCid] = struct{}{}
   180  			}
   181  		}
   182  		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   183  		defer cancel()
   184  		session := bex.GetSession(ctx)
   185  		for blobCid := range blobsToGet {
   186  			_, err := session.GetBlob(ctx, blobCid)
   187  			suite.Assert().NoError(err)
   188  		}
   189  	}
   190  }
   191  
   192  func (suite *BlobServiceTestSuite) TestHas() {
   193  	var blobChans []<-chan blobs.Blob
   194  	unreceivedBlobs := make([]map[cid.Cid]struct{}, len(suite.blobServices))
   195  
   196  	for i, bex := range suite.blobServices {
   197  		unreceivedBlobs[i] = make(map[cid.Cid]struct{})
   198  		// check that peers are notified when we have a new blob
   199  		var blobsToGet []cid.Cid
   200  		for j := 0; j < suite.numNodes; j++ {
   201  			if j != i {
   202  				blob := blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i)))
   203  				blobsToGet = append(blobsToGet, blob.Cid())
   204  				unreceivedBlobs[i][blob.Cid()] = struct{}{}
   205  			}
   206  		}
   207  
   208  		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   209  		defer cancel()
   210  
   211  		blobs := bex.GetBlobs(ctx, blobsToGet)
   212  		blobChans = append(blobChans, blobs)
   213  	}
   214  
   215  	// check that blobs are not received until Has is called by the server
   216  	suite.Require().Never(func() bool {
   217  		for _, blobChan := range blobChans {
   218  			select {
   219  			case _, ok := <-blobChan:
   220  				if ok {
   221  					return true
   222  				}
   223  			default:
   224  			}
   225  		}
   226  		return false
   227  	}, time.Second, 100*time.Millisecond)
   228  
   229  	for i, bex := range suite.blobServices {
   230  		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   231  		defer cancel()
   232  
   233  		err := bex.AddBlob(ctx, blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i))))
   234  		suite.Require().NoError(err)
   235  	}
   236  
   237  	for i, blobs := range blobChans {
   238  		for blob := range blobs {
   239  			delete(unreceivedBlobs[i], blob.Cid())
   240  		}
   241  		for c := range unreceivedBlobs[i] {
   242  			suite.T().Errorf("blob %v not received by node %v", c, i)
   243  		}
   244  	}
   245  }