@@ -8,18 +8,16 @@ import (
8
8
"io"
9
9
"net/http"
10
10
"strings"
11
- "time"
12
11
13
12
"github.com/ipfs/boxo/blockservice"
14
13
blockstore "github.com/ipfs/boxo/blockstore"
14
+ "github.com/ipfs/boxo/exchange/offline"
15
15
"github.com/ipfs/boxo/fetcher"
16
16
bsfetcher "github.com/ipfs/boxo/fetcher/impl/blockservice"
17
17
"github.com/ipfs/boxo/files"
18
18
"github.com/ipfs/boxo/ipld/merkledag"
19
19
ufile "github.com/ipfs/boxo/ipld/unixfs/file"
20
20
uio "github.com/ipfs/boxo/ipld/unixfs/io"
21
- "github.com/ipfs/boxo/ipns"
22
- "github.com/ipfs/boxo/namesys"
23
21
"github.com/ipfs/boxo/path"
24
22
"github.com/ipfs/boxo/path/resolver"
25
23
blocks "github.com/ipfs/go-block-format"
@@ -38,8 +36,6 @@ import (
38
36
"github.com/ipld/go-ipld-prime/traversal"
39
37
"github.com/ipld/go-ipld-prime/traversal/selector"
40
38
selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse"
41
- routinghelpers "github.com/libp2p/go-libp2p-routing-helpers"
42
- "github.com/libp2p/go-libp2p/core/routing"
43
39
mc "github.com/multiformats/go-multicodec"
44
40
45
41
// Ensure basic codecs are registered.
@@ -51,54 +47,18 @@ import (
51
47
52
48
// BlocksBackend is an [IPFSBackend] implementation based on a [blockservice.BlockService].
53
49
type BlocksBackend struct {
50
+ baseBackend
54
51
blockStore blockstore.Blockstore
55
52
blockService blockservice.BlockService
56
53
dagService format.DAGService
57
54
resolver resolver.Resolver
58
-
59
- // Optional routing system to handle /ipns addresses.
60
- namesys namesys.NameSystem
61
- routing routing.ValueStore
62
55
}
63
56
64
57
var _ IPFSBackend = (* BlocksBackend )(nil )
65
58
66
- type blocksBackendOptions struct {
67
- ns namesys.NameSystem
68
- vs routing.ValueStore
69
- r resolver.Resolver
70
- }
71
-
72
- // WithNameSystem sets the name system to use with the [BlocksBackend]. If not set
73
- // it will use the default DNSLink resolver generated by [NewDNSResolver] along
74
- // with any configured [routing.ValueStore].
75
- func WithNameSystem (ns namesys.NameSystem ) BlocksBackendOption {
76
- return func (opts * blocksBackendOptions ) error {
77
- opts .ns = ns
78
- return nil
79
- }
80
- }
81
-
82
- // WithValueStore sets the [routing.ValueStore] to use with the [BlocksBackend].
83
- func WithValueStore (vs routing.ValueStore ) BlocksBackendOption {
84
- return func (opts * blocksBackendOptions ) error {
85
- opts .vs = vs
86
- return nil
87
- }
88
- }
89
-
90
- // WithResolver sets the [resolver.Resolver] to use with the [BlocksBackend].
91
- func WithResolver (r resolver.Resolver ) BlocksBackendOption {
92
- return func (opts * blocksBackendOptions ) error {
93
- opts .r = r
94
- return nil
95
- }
96
- }
97
-
98
- type BlocksBackendOption func (options * blocksBackendOptions ) error
99
-
100
- func NewBlocksBackend (blockService blockservice.BlockService , opts ... BlocksBackendOption ) (* BlocksBackend , error ) {
101
- var compiledOptions blocksBackendOptions
59
+ // NewBlocksBackend creates a new [BlocksBackend] backed by a [blockservice.BlockService].
60
+ func NewBlocksBackend (blockService blockservice.BlockService , opts ... BackendOption ) (* BlocksBackend , error ) {
61
+ var compiledOptions backendOptions
102
62
for _ , o := range opts {
103
63
if err := o (& compiledOptions ); err != nil {
104
64
return nil , err
@@ -108,50 +68,51 @@ func NewBlocksBackend(blockService blockservice.BlockService, opts ...BlocksBack
108
68
// Setup the DAG services, which use the CAR block store.
109
69
dagService := merkledag .NewDAGService (blockService )
110
70
111
- // Setup a name system so that we are able to resolve /ipns links.
112
- var (
113
- ns namesys.NameSystem
114
- vs routing.ValueStore
115
- r resolver.Resolver
116
- )
117
-
118
- vs = compiledOptions .vs
119
- if vs == nil {
120
- vs = routinghelpers.Null {}
121
- }
122
-
123
- ns = compiledOptions .ns
124
- if ns == nil {
125
- dns , err := NewDNSResolver (nil , nil )
126
- if err != nil {
127
- return nil , err
128
- }
129
-
130
- ns , err = namesys .NewNameSystem (vs , namesys .WithDNSResolver (dns ))
131
- if err != nil {
132
- return nil , err
133
- }
134
- }
135
-
136
- r = compiledOptions .r
71
+ // Setup the [resolver.Resolver] if not provided.
72
+ r := compiledOptions .r
137
73
if r == nil {
138
- // Setup the UnixFS resolver.
139
74
fetcherCfg := bsfetcher .NewFetcherConfig (blockService )
140
75
fetcherCfg .PrototypeChooser = dagpb .AddSupportToChooser (bsfetcher .DefaultPrototypeChooser )
141
76
fetcher := fetcherCfg .WithReifier (unixfsnode .Reify )
142
77
r = resolver .NewBasicResolver (fetcher )
143
78
}
144
79
80
+ // Setup the [baseBackend] which takes care of some shared functionality, such
81
+ // as resolving /ipns links.
82
+ baseBackend , err := newBaseBackend (compiledOptions .vs , compiledOptions .ns )
83
+ if err != nil {
84
+ return nil , err
85
+ }
86
+
145
87
return & BlocksBackend {
88
+ baseBackend : baseBackend ,
146
89
blockStore : blockService .Blockstore (),
147
90
blockService : blockService ,
148
91
dagService : dagService ,
149
92
resolver : r ,
150
- routing : vs ,
151
- namesys : ns ,
152
93
}, nil
153
94
}
154
95
96
+ // NewRemoteBlocksBackend creates a new [BlocksBackend] backed by one or more
97
+ // gateways. These gateways must support RAW block requests and IPNS Record
98
+ // requests. See [NewRemoteBlockstore] and [NewRemoteValueStore] for more details.
99
+ //
100
+ // To create a more custom [BlocksBackend], please use [NewBlocksBackend] directly.
101
+ func NewRemoteBlocksBackend (gatewayURL []string , httpClient * http.Client , opts ... BackendOption ) (* BlocksBackend , error ) {
102
+ blockStore , err := NewRemoteBlockstore (gatewayURL , httpClient )
103
+ if err != nil {
104
+ return nil , err
105
+ }
106
+
107
+ valueStore , err := NewRemoteValueStore (gatewayURL , httpClient )
108
+ if err != nil {
109
+ return nil , err
110
+ }
111
+
112
+ blockService := blockservice .New (blockStore , offline .Exchange (blockStore ))
113
+ return NewBlocksBackend (blockService , append (opts , WithValueStore (valueStore ))... )
114
+ }
115
+
155
116
func (bb * BlocksBackend ) Get (ctx context.Context , path path.ImmutablePath , ranges ... ByteRange ) (ContentPathMetadata , * GetResponse , error ) {
156
117
md , nd , err := bb .getNode (ctx , path )
157
118
if err != nil {
@@ -367,9 +328,17 @@ func (bb *BlocksBackend) GetCAR(ctx context.Context, p path.ImmutablePath, param
367
328
unixfsnode .AddUnixFSReificationToLinkSystem (& lsys )
368
329
lsys .StorageReadOpener = blockOpener (ctx , blockGetter )
369
330
331
+ // First resolve the path since we always need to.
332
+ lastCid , remainder , err := pathResolver .ResolveToLastNode (ctx , p )
333
+ if err != nil {
334
+ // io.PipeWriter.CloseWithError always returns nil.
335
+ _ = w .CloseWithError (err )
336
+ return
337
+ }
338
+
370
339
// TODO: support selectors passed as request param: https://github.com/ipfs/kubo/issues/8769
371
340
// TODO: this is very slow if blocks are remote due to linear traversal. Do we need deterministic traversals here?
372
- carWriteErr := walkGatewaySimpleSelector (ctx , p , params , & lsys , pathResolver )
341
+ carWriteErr := walkGatewaySimpleSelector (ctx , lastCid , nil , remainder , params , & lsys )
373
342
374
343
// io.PipeWriter.CloseWithError always returns nil.
375
344
_ = w .CloseWithError (carWriteErr )
@@ -379,29 +348,49 @@ func (bb *BlocksBackend) GetCAR(ctx context.Context, p path.ImmutablePath, param
379
348
}
380
349
381
350
// walkGatewaySimpleSelector walks the subgraph described by the path and terminal element parameters
382
- func walkGatewaySimpleSelector (ctx context.Context , p path.ImmutablePath , params CarParams , lsys * ipld.LinkSystem , pathResolver resolver.Resolver ) error {
383
- // First resolve the path since we always need to.
384
- lastCid , remainder , err := pathResolver .ResolveToLastNode (ctx , p )
385
- if err != nil {
386
- return err
387
- }
388
-
351
+ func walkGatewaySimpleSelector (ctx context.Context , lastCid cid.Cid , terminalBlk blocks.Block , remainder []string , params CarParams , lsys * ipld.LinkSystem ) error {
389
352
lctx := ipld.LinkContext {Ctx : ctx }
390
353
pathTerminalCidLink := cidlink.Link {Cid : lastCid }
391
354
392
355
// If the scope is the block, now we only need to retrieve the root block of the last element of the path.
393
356
if params .Scope == DagScopeBlock {
394
- _ , err = lsys .LoadRaw (lctx , pathTerminalCidLink )
357
+ _ , err : = lsys .LoadRaw (lctx , pathTerminalCidLink )
395
358
return err
396
359
}
397
360
398
- // If we're asking for everything then give it
399
- if params .Scope == DagScopeAll {
400
- lastCidNode , err := lsys .Load (lctx , pathTerminalCidLink , basicnode .Prototype .Any )
361
+ pc := dagpb .AddSupportToChooser (func (lnk ipld.Link , lnkCtx ipld.LinkContext ) (ipld.NodePrototype , error ) {
362
+ if tlnkNd , ok := lnkCtx .LinkNode .(schema.TypedLinkNode ); ok {
363
+ return tlnkNd .LinkTargetNodePrototype (), nil
364
+ }
365
+ return basicnode .Prototype .Any , nil
366
+ })
367
+
368
+ np , err := pc (pathTerminalCidLink , lctx )
369
+ if err != nil {
370
+ return err
371
+ }
372
+
373
+ var lastCidNode datamodel.Node
374
+ if terminalBlk != nil {
375
+ decoder , err := lsys .DecoderChooser (pathTerminalCidLink )
401
376
if err != nil {
402
377
return err
403
378
}
379
+ nb := np .NewBuilder ()
380
+ blockData := terminalBlk .RawData ()
381
+ if err := decoder (nb , bytes .NewReader (blockData )); err != nil {
382
+ return err
383
+ }
384
+ lastCidNode = nb .Build ()
385
+ } else {
386
+ lastCidNode , err = lsys .Load (lctx , pathTerminalCidLink , np )
387
+ if err != nil {
388
+ return err
389
+ }
390
+ }
404
391
392
+ // If we're asking for everything then give it
393
+ if params .Scope == DagScopeAll {
405
394
sel , err := selector .ParseSelector (selectorparse .CommonSelector_ExploreAllRecursively )
406
395
if err != nil {
407
396
return err
@@ -427,23 +416,6 @@ func walkGatewaySimpleSelector(ctx context.Context, p path.ImmutablePath, params
427
416
// From now on, dag-scope=entity!
428
417
// Since we need more of the graph load it to figure out what we have
429
418
// This includes determining if the terminal node is UnixFS or not
430
- pc := dagpb .AddSupportToChooser (func (lnk ipld.Link , lnkCtx ipld.LinkContext ) (ipld.NodePrototype , error ) {
431
- if tlnkNd , ok := lnkCtx .LinkNode .(schema.TypedLinkNode ); ok {
432
- return tlnkNd .LinkTargetNodePrototype (), nil
433
- }
434
- return basicnode .Prototype .Any , nil
435
- })
436
-
437
- np , err := pc (pathTerminalCidLink , lctx )
438
- if err != nil {
439
- return err
440
- }
441
-
442
- lastCidNode , err := lsys .Load (lctx , pathTerminalCidLink , np )
443
- if err != nil {
444
- return err
445
- }
446
-
447
419
if pbn , ok := lastCidNode .(dagpb.PBNode ); ! ok {
448
420
// If it's not valid dag-pb then we're done
449
421
return nil
@@ -630,55 +602,6 @@ func (bb *BlocksBackend) getPathRoots(ctx context.Context, contentPath path.Immu
630
602
return pathRoots , lastPath , remainder , nil
631
603
}
632
604
633
- func (bb * BlocksBackend ) ResolveMutable (ctx context.Context , p path.Path ) (path.ImmutablePath , time.Duration , time.Time , error ) {
634
- switch p .Namespace () {
635
- case path .IPNSNamespace :
636
- res , err := namesys .Resolve (ctx , bb .namesys , p )
637
- if err != nil {
638
- return path.ImmutablePath {}, 0 , time.Time {}, err
639
- }
640
- ip , err := path .NewImmutablePath (res .Path )
641
- if err != nil {
642
- return path.ImmutablePath {}, 0 , time.Time {}, err
643
- }
644
- return ip , res .TTL , res .LastMod , nil
645
- case path .IPFSNamespace :
646
- ip , err := path .NewImmutablePath (p )
647
- return ip , 0 , time.Time {}, err
648
- default :
649
- return path.ImmutablePath {}, 0 , time.Time {}, NewErrorStatusCode (fmt .Errorf ("unsupported path namespace: %s" , p .Namespace ()), http .StatusNotImplemented )
650
- }
651
- }
652
-
653
- func (bb * BlocksBackend ) GetIPNSRecord (ctx context.Context , c cid.Cid ) ([]byte , error ) {
654
- if bb .routing == nil {
655
- return nil , NewErrorStatusCode (errors .New ("IPNS Record responses are not supported by this gateway" ), http .StatusNotImplemented )
656
- }
657
-
658
- name , err := ipns .NameFromCid (c )
659
- if err != nil {
660
- return nil , NewErrorStatusCode (err , http .StatusBadRequest )
661
- }
662
-
663
- return bb .routing .GetValue (ctx , string (name .RoutingKey ()))
664
- }
665
-
666
- func (bb * BlocksBackend ) GetDNSLinkRecord (ctx context.Context , hostname string ) (path.Path , error ) {
667
- if bb .namesys != nil {
668
- p , err := path .NewPath ("/ipns/" + hostname )
669
- if err != nil {
670
- return nil , err
671
- }
672
- res , err := bb .namesys .Resolve (ctx , p , namesys .ResolveWithDepth (1 ))
673
- if err == namesys .ErrResolveRecursion {
674
- err = nil
675
- }
676
- return res .Path , err
677
- }
678
-
679
- return nil , NewErrorStatusCode (errors .New ("not implemented" ), http .StatusNotImplemented )
680
- }
681
-
682
605
func (bb * BlocksBackend ) IsCached (ctx context.Context , p path.Path ) bool {
683
606
rp , _ , err := bb .resolvePath (ctx , p )
684
607
if err != nil {
@@ -711,11 +634,10 @@ func (bb *BlocksBackend) ResolvePath(ctx context.Context, path path.ImmutablePat
711
634
func (bb * BlocksBackend ) resolvePath (ctx context.Context , p path.Path ) (path.ImmutablePath , []string , error ) {
712
635
var err error
713
636
if p .Namespace () == path .IPNSNamespace {
714
- res , err := namesys . Resolve (ctx , bb . namesys , p )
637
+ p , _ , _ , err = bb . baseBackend . ResolveMutable (ctx , p )
715
638
if err != nil {
716
639
return path.ImmutablePath {}, nil , err
717
640
}
718
- p = res .Path
719
641
}
720
642
721
643
if p .Namespace () != path .IPFSNamespace {
0 commit comments