github.com/filecoin-project/lassie@v0.23.0/pkg/internal/itest/direct_fetch_test.go (about)

     1  package itest
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io"
     7  	"math/rand"
     8  	"os"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/filecoin-project/lassie/pkg/internal/itest/mocknet"
    13  	"github.com/filecoin-project/lassie/pkg/internal/lp2ptransports"
    14  	"github.com/filecoin-project/lassie/pkg/lassie"
    15  	"github.com/filecoin-project/lassie/pkg/retriever"
    16  	"github.com/filecoin-project/lassie/pkg/types"
    17  	"github.com/ipfs/go-cid"
    18  	"github.com/ipfs/go-unixfsnode"
    19  	unixfs "github.com/ipfs/go-unixfsnode/testutil"
    20  	carv2 "github.com/ipld/go-car/v2"
    21  	"github.com/ipld/go-car/v2/storage"
    22  	"github.com/ipld/go-ipld-prime/codec/dagcbor"
    23  	cidlink "github.com/ipld/go-ipld-prime/linking/cid"
    24  	trustlessutils "github.com/ipld/go-trustless-utils"
    25  	host "github.com/libp2p/go-libp2p/core/host"
    26  	"github.com/libp2p/go-libp2p/core/network"
    27  	"github.com/libp2p/go-libp2p/core/peer"
    28  	lpmock "github.com/libp2p/go-libp2p/p2p/net/mock"
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  const (
    33  	bitswapDirect    = 0
    34  	graphsyncDirect  = 1
    35  	httpDirect       = 2
    36  	transportsDirect = 3
    37  )
    38  
    39  func TestDirectFetch(t *testing.T) {
    40  	testCases := []struct {
    41  		name       string
    42  		directPeer int
    43  	}{
    44  		{
    45  			name:       "direct bitswap peer",
    46  			directPeer: bitswapDirect,
    47  		},
    48  		{
    49  			name:       "direct graphsync peer",
    50  			directPeer: graphsyncDirect,
    51  		},
    52  		{
    53  			name:       "direct http peer",
    54  			directPeer: graphsyncDirect,
    55  		},
    56  		{
    57  			name:       "peer responding on transports protocol",
    58  			directPeer: transportsDirect,
    59  		},
    60  	}
    61  
    62  	for _, testCase := range testCases {
    63  		t.Run(testCase.name, func(t *testing.T) {
    64  			req := require.New(t)
    65  			ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    66  			defer cancel()
    67  
    68  			rndSeed := time.Now().UTC().UnixNano()
    69  			t.Logf("random seed: %d", rndSeed)
    70  			var rndReader io.Reader = rand.New(rand.NewSource(rndSeed))
    71  
    72  			mrn := mocknet.NewMockRetrievalNet(ctx, t)
    73  			mrn.AddGraphsyncPeers(1)
    74  			mrn.AddBitswapPeers(1)
    75  			mrn.AddHttpPeers(1)
    76  
    77  			// generate separate 4MiB random unixfs file DAGs on both peers
    78  
    79  			// graphsync peer (0)
    80  			graphsyncMAs, err := peer.AddrInfoToP2pAddrs(mrn.Remotes[0].AddrInfo())
    81  			req.NoError(err)
    82  			srcData1 := unixfs.GenerateFile(t, mrn.Remotes[0].LinkSystem, rndReader, 4<<20)
    83  			mocknet.SetupRetrieval(t, mrn.Remotes[0])
    84  
    85  			// bitswap peer (1)
    86  			srcData2 := unixfs.GenerateFile(t, mrn.Remotes[1].LinkSystem, bytes.NewReader(srcData1.Content), 4<<20)
    87  			req.Equal(srcData2.Root, srcData1.Root)
    88  			bitswapMAs, err := peer.AddrInfoToP2pAddrs(mrn.Remotes[1].AddrInfo())
    89  			req.NoError(err)
    90  
    91  			// http peer (1)
    92  			srcData3 := unixfs.GenerateFile(t, mrn.Remotes[2].LinkSystem, bytes.NewReader(srcData1.Content), 4<<20)
    93  			req.Equal(srcData3.Root, srcData1.Root)
    94  			httpMAs, err := peer.AddrInfoToP2pAddrs(mrn.Remotes[2].AddrInfo())
    95  			req.NoError(err)
    96  
    97  			transportsAddr, clear := handleTransports(t, mrn.MN, []lp2ptransports.Protocol{
    98  				{
    99  					Name:      "bitswap",
   100  					Addresses: bitswapMAs,
   101  				},
   102  				{
   103  					Name:      "libp2p",
   104  					Addresses: graphsyncMAs,
   105  				},
   106  				{
   107  					Name:      "http",
   108  					Addresses: httpMAs,
   109  				},
   110  			})
   111  			req.NoError(mrn.MN.LinkAll())
   112  			defer clear()
   113  
   114  			var addr peer.AddrInfo
   115  			switch testCase.directPeer {
   116  			case graphsyncDirect:
   117  				addr = *mrn.Remotes[0].AddrInfo()
   118  			case bitswapDirect:
   119  				addr = *mrn.Remotes[1].AddrInfo()
   120  			case httpDirect:
   121  				addr = *mrn.Remotes[2].AddrInfo()
   122  			case transportsDirect:
   123  				addr = transportsAddr
   124  			default:
   125  				req.FailNow("unrecognized direct peer test")
   126  			}
   127  
   128  			directFinder := retriever.NewDirectCandidateSource([]types.Provider{{Peer: addr, Protocols: nil}}, retriever.WithLibp2pCandidateDiscovery(mrn.Self))
   129  			lassie, err := lassie.NewLassie(ctx, lassie.WithCandidateSource(directFinder), lassie.WithHost(mrn.Self), lassie.WithGlobalTimeout(5*time.Second))
   130  			req.NoError(err)
   131  			outFile, err := os.CreateTemp(t.TempDir(), "lassie-test-")
   132  			req.NoError(err)
   133  			defer func() {
   134  				req.NoError(outFile.Close())
   135  			}()
   136  			outCar, err := storage.NewReadableWritable(outFile, []cid.Cid{srcData1.Root}, carv2.WriteAsCarV1(true))
   137  			req.NoError(err)
   138  			request, err := types.NewRequestForPath(outCar, srcData1.Root, "", trustlessutils.DagScopeAll, nil)
   139  			req.NoError(err)
   140  			_, err = lassie.Fetch(ctx, request)
   141  			req.NoError(err)
   142  			err = outCar.Finalize()
   143  			req.NoError(err)
   144  			outFile.Seek(0, io.SeekStart)
   145  			// Open the CAR bytes as read-only storage
   146  			reader, err := storage.OpenReadable(outFile)
   147  			req.NoError(err)
   148  
   149  			// Load our UnixFS data and compare it to the original
   150  			linkSys := cidlink.DefaultLinkSystem()
   151  			linkSys.SetReadStorage(reader)
   152  			linkSys.NodeReifier = unixfsnode.Reify
   153  			linkSys.TrustedStorage = true
   154  			gotDir := unixfs.ToDirEntry(t, linkSys, srcData1.Root, true)
   155  			unixfs.CompareDirEntries(t, srcData1, gotDir)
   156  		})
   157  	}
   158  }
   159  
   160  type transportsListener struct {
   161  	t         *testing.T
   162  	host      host.Host
   163  	protocols []lp2ptransports.Protocol
   164  }
   165  
   166  func handleTransports(t *testing.T, mn lpmock.Mocknet, protocols []lp2ptransports.Protocol) (peer.AddrInfo, func()) {
   167  	h, err := mn.GenPeer()
   168  	require.NoError(t, err)
   169  
   170  	p := &transportsListener{t, h, protocols}
   171  	h.SetStreamHandler(lp2ptransports.TransportsProtocolID, p.handleNewQueryStream)
   172  	return peer.AddrInfo{
   173  			ID:    h.ID(),
   174  			Addrs: h.Addrs(),
   175  		}, func() {
   176  			h.RemoveStreamHandler(lp2ptransports.TransportsProtocolID)
   177  		}
   178  }
   179  
   180  // Called when the client opens a libp2p stream
   181  func (l *transportsListener) handleNewQueryStream(s network.Stream) {
   182  	defer s.Close()
   183  	response := lp2ptransports.QueryResponse{Protocols: l.protocols}
   184  	// Write the response to the client
   185  	err := lp2ptransports.BindnodeRegistry.TypeToWriter(&response, s, dagcbor.Encode)
   186  	require.NoError(l.t, err)
   187  }