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 }