master
go 266 lines 7.49 KB
Raw
1 /*
2 **NOTE: this package is experimental.**
3
4 Package coreapi provides direct access to the core commands in IPFS. If you are
5 embedding IPFS directly in your Go program, this package is the public
6 interface you should use to read and write files or otherwise control IPFS.
7
8 If you are running IPFS as a separate process, you should use `client/rpc` to
9 work with it via HTTP.
10 */
11 package coreapi
12
13 import (
14 "context"
15 "errors"
16 "fmt"
17
18 bserv "github.com/ipfs/boxo/blockservice"
19 blockstore "github.com/ipfs/boxo/blockstore"
20 exchange "github.com/ipfs/boxo/exchange"
21 offlinexch "github.com/ipfs/boxo/exchange/offline"
22 "github.com/ipfs/boxo/fetcher"
23 dag "github.com/ipfs/boxo/ipld/merkledag"
24 pathresolver "github.com/ipfs/boxo/path/resolver"
25 pin "github.com/ipfs/boxo/pinning/pinner"
26 offlineroute "github.com/ipfs/boxo/routing/offline"
27 ipld "github.com/ipfs/go-ipld-format"
28 "github.com/ipfs/kubo/config"
29 coreiface "github.com/ipfs/kubo/core/coreiface"
30 "github.com/ipfs/kubo/core/coreiface/options"
31 "github.com/ipfs/kubo/internal/fusemount"
32 pubsub "github.com/libp2p/go-libp2p-pubsub"
33 record "github.com/libp2p/go-libp2p-record"
34 ci "github.com/libp2p/go-libp2p/core/crypto"
35 p2phost "github.com/libp2p/go-libp2p/core/host"
36 peer "github.com/libp2p/go-libp2p/core/peer"
37 pstore "github.com/libp2p/go-libp2p/core/peerstore"
38 routing "github.com/libp2p/go-libp2p/core/routing"
39 madns "github.com/multiformats/go-multiaddr-dns"
40
41 "github.com/ipfs/boxo/namesys"
42 "github.com/ipfs/kubo/core"
43 "github.com/ipfs/kubo/core/node"
44 "github.com/ipfs/kubo/repo"
45 )
46
47 type CoreAPI struct {
48 nctx context.Context
49
50 identity peer.ID
51 privateKey ci.PrivKey
52
53 repo repo.Repo
54 blockstore blockstore.GCBlockstore
55 baseBlocks blockstore.Blockstore
56 pinning pin.Pinner
57
58 blocks bserv.BlockService
59 dag ipld.DAGService
60 ipldFetcherFactory fetcher.Factory
61 unixFSFetcherFactory fetcher.Factory
62 peerstore pstore.Peerstore
63 peerHost p2phost.Host
64 recordValidator record.Validator
65 exchange exchange.Interface
66
67 namesys namesys.NameSystem
68 routing routing.Routing
69 dnsResolver *madns.Resolver
70 ipldPathResolver pathresolver.Resolver
71 unixFSPathResolver pathresolver.Resolver
72
73 provider node.DHTProvider
74
75 pubSub *pubsub.PubSub
76
77 checkPublishAllowed func(ctx context.Context) error
78 checkOnline func(allowOffline bool) error
79
80 // ONLY for re-applying options in WithOptions, DO NOT USE ANYWHERE ELSE
81 nd *core.IpfsNode
82 parentOpts options.ApiSettings
83 }
84
85 // NewCoreAPI creates new instance of IPFS CoreAPI backed by go-ipfs Node.
86 func NewCoreAPI(n *core.IpfsNode, opts ...options.ApiOption) (coreiface.CoreAPI, error) {
87 parentOpts, err := options.ApiOptions()
88 if err != nil {
89 return nil, err
90 }
91
92 return (&CoreAPI{nd: n, parentOpts: *parentOpts}).WithOptions(opts...)
93 }
94
95 // Unixfs returns the UnixfsAPI interface implementation backed by the go-ipfs node
96 func (api *CoreAPI) Unixfs() coreiface.UnixfsAPI {
97 return (*UnixfsAPI)(api)
98 }
99
100 // Block returns the BlockAPI interface implementation backed by the go-ipfs node
101 func (api *CoreAPI) Block() coreiface.BlockAPI {
102 return (*BlockAPI)(api)
103 }
104
105 // Dag returns the DagAPI interface implementation backed by the go-ipfs node
106 func (api *CoreAPI) Dag() coreiface.APIDagService {
107 return &dagAPI{
108 api.dag,
109 api,
110 }
111 }
112
113 // Name returns the NameAPI interface implementation backed by the go-ipfs node
114 func (api *CoreAPI) Name() coreiface.NameAPI {
115 return (*NameAPI)(api)
116 }
117
118 // Key returns the KeyAPI interface implementation backed by the go-ipfs node
119 func (api *CoreAPI) Key() coreiface.KeyAPI {
120 return (*KeyAPI)(api)
121 }
122
123 // Object returns the ObjectAPI interface implementation backed by the go-ipfs node
124 func (api *CoreAPI) Object() coreiface.ObjectAPI {
125 return (*ObjectAPI)(api)
126 }
127
128 // Pin returns the PinAPI interface implementation backed by the go-ipfs node
129 func (api *CoreAPI) Pin() coreiface.PinAPI {
130 return (*PinAPI)(api)
131 }
132
133 // Swarm returns the SwarmAPI interface implementation backed by the go-ipfs node
134 func (api *CoreAPI) Swarm() coreiface.SwarmAPI {
135 return (*SwarmAPI)(api)
136 }
137
138 // PubSub returns the PubSubAPI interface implementation backed by the go-ipfs node
139 func (api *CoreAPI) PubSub() coreiface.PubSubAPI {
140 return (*PubSubAPI)(api)
141 }
142
143 // Routing returns the RoutingAPI interface implementation backed by the kubo node
144 func (api *CoreAPI) Routing() coreiface.RoutingAPI {
145 return (*RoutingAPI)(api)
146 }
147
148 // WithOptions returns api with global options applied
149 func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, error) {
150 settings := api.parentOpts // make sure to copy
151 _, err := options.ApiOptionsTo(&settings, opts...)
152 if err != nil {
153 return nil, err
154 }
155
156 if api.nd == nil {
157 return nil, errors.New("cannot apply options to api without node")
158 }
159
160 n := api.nd
161
162 subAPI := &CoreAPI{
163 nctx: n.Context(),
164
165 identity: n.Identity,
166 privateKey: n.PrivateKey,
167
168 repo: n.Repo,
169 blockstore: n.Blockstore,
170 baseBlocks: n.BaseBlocks,
171 pinning: n.Pinning,
172
173 blocks: n.Blocks,
174 dag: n.DAG,
175 ipldFetcherFactory: n.IPLDFetcherFactory,
176 unixFSFetcherFactory: n.UnixFSFetcherFactory,
177
178 peerstore: n.Peerstore,
179 peerHost: n.PeerHost,
180 namesys: n.Namesys,
181 recordValidator: n.RecordValidator,
182 exchange: n.Exchange,
183 routing: n.Routing,
184 dnsResolver: n.DNSResolver,
185 ipldPathResolver: n.IPLDPathResolver,
186 unixFSPathResolver: n.UnixFSPathResolver,
187
188 provider: n.Provider,
189
190 pubSub: n.PubSub,
191
192 nd: n,
193 parentOpts: settings,
194 }
195
196 subAPI.checkOnline = func(allowOffline bool) error {
197 if !n.IsOnline && !allowOffline {
198 return coreiface.ErrOffline
199 }
200 return nil
201 }
202
203 subAPI.checkPublishAllowed = func(ctx context.Context) error {
204 if fusemount.IsPublish(ctx) {
205 return nil
206 }
207 if n.Mounts.Ipns != nil && n.Mounts.Ipns.IsActive() {
208 return errors.New("cannot manually publish while IPNS is mounted")
209 }
210 return nil
211 }
212
213 cfg, err := n.Repo.Config()
214 if err != nil {
215 return nil, err
216 }
217
218 if settings.Offline {
219 cs := cfg.Ipns.ResolveCacheSize
220 if cs == 0 {
221 cs = node.DefaultIpnsCacheSize
222 }
223 if cs < 0 {
224 return nil, errors.New("cannot specify negative resolve cache size")
225 }
226
227 nsOptions := []namesys.Option{
228 namesys.WithDatastore(subAPI.repo.Datastore()),
229 namesys.WithDNSResolver(subAPI.dnsResolver),
230 namesys.WithCache(cs),
231 namesys.WithMaxCacheTTL(cfg.Ipns.MaxCacheTTL.WithDefault(config.DefaultIpnsMaxCacheTTL)),
232 }
233
234 subAPI.routing = offlineroute.NewOfflineRouter(subAPI.repo.Datastore(), subAPI.recordValidator)
235
236 subAPI.namesys, err = namesys.NewNameSystem(subAPI.routing, nsOptions...)
237 if err != nil {
238 return nil, fmt.Errorf("error constructing namesys: %w", err)
239 }
240
241 subAPI.peerstore = nil
242 subAPI.peerHost = nil
243 subAPI.recordValidator = nil
244 }
245
246 if settings.Offline || !settings.FetchBlocks {
247 subAPI.exchange = offlinexch.Exchange(subAPI.blockstore)
248 subAPI.blocks = bserv.New(subAPI.blockstore, subAPI.exchange,
249 bserv.WriteThrough(cfg.Datastore.WriteThrough.WithDefault(config.DefaultWriteThrough)),
250 )
251 subAPI.dag = dag.NewDAGService(subAPI.blocks)
252 }
253
254 return subAPI, nil
255 }
256
257 // getSession returns new api backed by the same node with a read-only session DAG
258 func (api *CoreAPI) getSession(ctx context.Context) *CoreAPI {
259 sesAPI := *api
260
261 // TODO: We could also apply this to api.blocks, and compose into writable api,
262 // but this requires some changes in blockservice/merkledag
263 sesAPI.dag = dag.NewReadOnlyDagService(dag.NewSession(ctx, api.dag))
264
265 return &sesAPI
266 }