| 1 | /* |
| 2 | Package core implements the IpfsNode object and related methods. |
| 3 | |
| 4 | Packages underneath core/ provide a (relatively) stable, low-level API |
| 5 | to carry out most IPFS-related tasks. For more details on the other |
| 6 | interfaces and how core/... fits into the bigger IPFS picture, see: |
| 7 | |
| 8 | $ godoc github.com/ipfs/go-ipfs |
| 9 | */ |
| 10 | package core |
| 11 | |
| 12 | import ( |
| 13 | "context" |
| 14 | "encoding/json" |
| 15 | "io" |
| 16 | "time" |
| 17 | |
| 18 | "github.com/ipfs/boxo/filestore" |
| 19 | pin "github.com/ipfs/boxo/pinning/pinner" |
| 20 | "github.com/ipfs/go-datastore" |
| 21 | |
| 22 | bitswap "github.com/ipfs/boxo/bitswap" |
| 23 | bserv "github.com/ipfs/boxo/blockservice" |
| 24 | bstore "github.com/ipfs/boxo/blockstore" |
| 25 | exchange "github.com/ipfs/boxo/exchange" |
| 26 | "github.com/ipfs/boxo/fetcher" |
| 27 | mfs "github.com/ipfs/boxo/mfs" |
| 28 | pathresolver "github.com/ipfs/boxo/path/resolver" |
| 29 | provider "github.com/ipfs/boxo/provider" |
| 30 | ipld "github.com/ipfs/go-ipld-format" |
| 31 | logging "github.com/ipfs/go-log/v2" |
| 32 | ddht "github.com/libp2p/go-libp2p-kad-dht/dual" |
| 33 | "github.com/libp2p/go-libp2p-kad-dht/fullrt" |
| 34 | pubsub "github.com/libp2p/go-libp2p-pubsub" |
| 35 | psrouter "github.com/libp2p/go-libp2p-pubsub-router" |
| 36 | record "github.com/libp2p/go-libp2p-record" |
| 37 | routinghelpers "github.com/libp2p/go-libp2p-routing-helpers" |
| 38 | connmgr "github.com/libp2p/go-libp2p/core/connmgr" |
| 39 | ic "github.com/libp2p/go-libp2p/core/crypto" |
| 40 | p2phost "github.com/libp2p/go-libp2p/core/host" |
| 41 | metrics "github.com/libp2p/go-libp2p/core/metrics" |
| 42 | "github.com/libp2p/go-libp2p/core/network" |
| 43 | peer "github.com/libp2p/go-libp2p/core/peer" |
| 44 | pstore "github.com/libp2p/go-libp2p/core/peerstore" |
| 45 | routing "github.com/libp2p/go-libp2p/core/routing" |
| 46 | "github.com/libp2p/go-libp2p/p2p/discovery/mdns" |
| 47 | p2pbhost "github.com/libp2p/go-libp2p/p2p/host/basic" |
| 48 | ma "github.com/multiformats/go-multiaddr" |
| 49 | madns "github.com/multiformats/go-multiaddr-dns" |
| 50 | |
| 51 | "github.com/ipfs/boxo/bootstrap" |
| 52 | "github.com/ipfs/boxo/namesys" |
| 53 | ipnsrp "github.com/ipfs/boxo/namesys/republisher" |
| 54 | "github.com/ipfs/boxo/peering" |
| 55 | "github.com/ipfs/kubo/config" |
| 56 | "github.com/ipfs/kubo/core/node" |
| 57 | "github.com/ipfs/kubo/core/node/libp2p" |
| 58 | "github.com/ipfs/kubo/fuse/mount" |
| 59 | "github.com/ipfs/kubo/p2p" |
| 60 | "github.com/ipfs/kubo/repo" |
| 61 | irouting "github.com/ipfs/kubo/routing" |
| 62 | ) |
| 63 | |
| 64 | var log = logging.Logger("core") |
| 65 | |
| 66 | // IpfsNode is IPFS Core module. It represents an IPFS instance. |
| 67 | type IpfsNode struct { |
| 68 | // Self |
| 69 | Identity peer.ID // the local node's identity |
| 70 | |
| 71 | Repo repo.Repo |
| 72 | |
| 73 | // Local node |
| 74 | Pinning pin.Pinner // the pinning manager |
| 75 | Mounts Mounts `optional:"true"` // current mount state, if any. |
| 76 | PrivateKey ic.PrivKey `optional:"true"` // the local node's private Key |
| 77 | PNetFingerprint libp2p.PNetFingerprint `optional:"true"` // fingerprint of private network |
| 78 | |
| 79 | // Services |
| 80 | Peerstore pstore.Peerstore `optional:"true"` // storage for other Peer instances |
| 81 | Blockstore bstore.GCBlockstore // the block store (lower level) |
| 82 | Filestore *filestore.Filestore `optional:"true"` // the filestore blockstore |
| 83 | BaseBlocks node.BaseBlocks // the raw blockstore, no filestore wrapping |
| 84 | GCLocker bstore.GCLocker // the locker used to protect the blockstore during gc |
| 85 | Blocks bserv.BlockService // the block service, get/add blocks. |
| 86 | DAG ipld.DAGService // the merkle dag service, get/add objects. |
| 87 | IPLDFetcherFactory fetcher.Factory `name:"ipldFetcher"` // fetcher that paths over the IPLD data model |
| 88 | UnixFSFetcherFactory fetcher.Factory `name:"unixfsFetcher"` // fetcher that interprets UnixFS data |
| 89 | OfflineIPLDFetcherFactory fetcher.Factory `name:"offlineIpldFetcher"` // fetcher that paths over the IPLD data model without fetching new blocks |
| 90 | OfflineUnixFSFetcherFactory fetcher.Factory `name:"offlineUnixfsFetcher"` // fetcher that interprets UnixFS data without fetching new blocks |
| 91 | Reporter *metrics.BandwidthCounter `optional:"true"` |
| 92 | Discovery mdns.Service `optional:"true"` |
| 93 | FilesRoot *mfs.Root |
| 94 | RecordValidator record.Validator |
| 95 | |
| 96 | // Online |
| 97 | PeerHost p2phost.Host `optional:"true"` // the network host (server+client) |
| 98 | Peering *peering.PeeringService `optional:"true"` |
| 99 | Filters *ma.Filters `optional:"true"` |
| 100 | Bootstrapper io.Closer `optional:"true"` // the periodic bootstrapper |
| 101 | ContentDiscovery routing.ContentDiscovery `optional:"true"` // the discovery part of the routing system |
| 102 | DNSResolver *madns.Resolver // the DNS resolver |
| 103 | IPLDPathResolver pathresolver.Resolver `name:"ipldPathResolver"` // The IPLD path resolver |
| 104 | UnixFSPathResolver pathresolver.Resolver `name:"unixFSPathResolver"` // The UnixFS path resolver |
| 105 | OfflineIPLDPathResolver pathresolver.Resolver `name:"offlineIpldPathResolver"` // The IPLD path resolver that uses only locally available blocks |
| 106 | OfflineUnixFSPathResolver pathresolver.Resolver `name:"offlineUnixFSPathResolver"` // The UnixFS path resolver that uses only locally available blocks |
| 107 | Exchange exchange.Interface // the block exchange + strategy |
| 108 | Bitswap *bitswap.Bitswap `optional:"true"` // The Bitswap instance |
| 109 | Namesys namesys.NameSystem // the name system, resolves paths to hashes |
| 110 | ProvidingStrategy config.ProvideStrategy `optional:"true"` |
| 111 | ProvidingKeyChanFunc provider.KeyChanFunc `optional:"true"` |
| 112 | IpnsRepub *ipnsrp.Republisher `optional:"true"` |
| 113 | ResourceManager network.ResourceManager `optional:"true"` |
| 114 | |
| 115 | PubSub *pubsub.PubSub `optional:"true"` |
| 116 | PSRouter *psrouter.PubsubValueStore `optional:"true"` |
| 117 | |
| 118 | Routing irouting.ProvideManyRouter `optional:"true"` // the routing system. recommend ipfs-dht |
| 119 | Provider node.DHTProvider // the value provider system |
| 120 | DHT *ddht.DHT `optional:"true"` |
| 121 | DHTClient routing.Routing `name:"dhtc" optional:"true"` |
| 122 | |
| 123 | P2P *p2p.P2P `optional:"true"` |
| 124 | |
| 125 | ctx context.Context |
| 126 | |
| 127 | stop func() error |
| 128 | |
| 129 | // Flags |
| 130 | IsOnline bool `optional:"true"` // Online is set when networking is enabled. |
| 131 | IsDaemon bool `optional:"true"` // Daemon is set when running on a long-running daemon. |
| 132 | } |
| 133 | |
| 134 | // Mounts defines what the node's mount state is. This should |
| 135 | // perhaps be moved to the daemon or mount. It's here because |
| 136 | // it needs to be accessible across daemon requests. |
| 137 | type Mounts struct { |
| 138 | Ipfs mount.Mount |
| 139 | Ipns mount.Mount |
| 140 | Mfs mount.Mount |
| 141 | } |
| 142 | |
| 143 | // Close calls Close() on the App object |
| 144 | func (n *IpfsNode) Close() error { |
| 145 | return n.stop() |
| 146 | } |
| 147 | |
| 148 | // HasActiveDHTClient checks if the node's DHT client is active and usable for DHT operations. |
| 149 | // |
| 150 | // Returns false for: |
| 151 | // - nil DHTClient |
| 152 | // - typed nil pointers (e.g., (*ddht.DHT)(nil)) |
| 153 | // - no-op routers (routinghelpers.Null) |
| 154 | // |
| 155 | // Note: This method only checks for known DHT client types (ddht.DHT, fullrt.FullRT). |
| 156 | // Custom routing.Routing implementations are not explicitly validated. |
| 157 | // |
| 158 | // This method prevents the "typed nil interface" bug where an interface contains |
| 159 | // a nil pointer of a concrete type, which passes nil checks but panics when methods |
| 160 | // are called. |
| 161 | func (n *IpfsNode) HasActiveDHTClient() bool { |
| 162 | if n.DHTClient == nil { |
| 163 | return false |
| 164 | } |
| 165 | |
| 166 | // Check for no-op router (Routing.Type=none) |
| 167 | if _, ok := n.DHTClient.(routinghelpers.Null); ok { |
| 168 | return false |
| 169 | } |
| 170 | |
| 171 | // Check for typed nil *ddht.DHT (common when Routing.Type=delegated or HTTP-only) |
| 172 | if d, ok := n.DHTClient.(*ddht.DHT); ok && d == nil { |
| 173 | return false |
| 174 | } |
| 175 | |
| 176 | // Check for typed nil *fullrt.FullRT (accelerated DHT client) |
| 177 | if f, ok := n.DHTClient.(*fullrt.FullRT); ok && f == nil { |
| 178 | return false |
| 179 | } |
| 180 | |
| 181 | return true |
| 182 | } |
| 183 | |
| 184 | // Context returns the IpfsNode context |
| 185 | func (n *IpfsNode) Context() context.Context { |
| 186 | if n.ctx == nil { |
| 187 | n.ctx = context.TODO() |
| 188 | } |
| 189 | return n.ctx |
| 190 | } |
| 191 | |
| 192 | // Bootstrap will set and call the IpfsNodes bootstrap function. |
| 193 | func (n *IpfsNode) Bootstrap(cfg bootstrap.BootstrapConfig) error { |
| 194 | // TODO what should return value be when in offlineMode? |
| 195 | if n.Routing == nil { |
| 196 | return nil |
| 197 | } |
| 198 | |
| 199 | if n.Bootstrapper != nil { |
| 200 | n.Bootstrapper.Close() // stop previous bootstrap process. |
| 201 | } |
| 202 | |
| 203 | // if the caller did not specify a bootstrap peer function, get the |
| 204 | // freshest bootstrap peers from config. this responds to live changes. |
| 205 | if cfg.BootstrapPeers == nil { |
| 206 | cfg.BootstrapPeers = func() []peer.AddrInfo { |
| 207 | ps, err := n.loadBootstrapPeers() |
| 208 | if err != nil { |
| 209 | log.Warn("failed to parse bootstrap peers from config") |
| 210 | return nil |
| 211 | } |
| 212 | return ps |
| 213 | } |
| 214 | } |
| 215 | if load, _ := cfg.BackupPeers(); load == nil { |
| 216 | save := func(ctx context.Context, peerList []peer.AddrInfo) { |
| 217 | err := n.saveTempBootstrapPeers(ctx, peerList) |
| 218 | if err != nil { |
| 219 | log.Warnf("saveTempBootstrapPeers failed: %s", err) |
| 220 | return |
| 221 | } |
| 222 | } |
| 223 | load = func(ctx context.Context) []peer.AddrInfo { |
| 224 | peerList, err := n.loadTempBootstrapPeers(ctx) |
| 225 | if err != nil { |
| 226 | log.Warnf("loadTempBootstrapPeers failed: %s", err) |
| 227 | return nil |
| 228 | } |
| 229 | return peerList |
| 230 | } |
| 231 | cfg.SetBackupPeers(load, save) |
| 232 | } |
| 233 | |
| 234 | repoConf, err := n.Repo.Config() |
| 235 | if err != nil { |
| 236 | return err |
| 237 | } |
| 238 | if repoConf.Internal.BackupBootstrapInterval != nil { |
| 239 | cfg.BackupBootstrapInterval = repoConf.Internal.BackupBootstrapInterval.WithDefault(time.Hour) |
| 240 | } |
| 241 | |
| 242 | n.Bootstrapper, err = bootstrap.Bootstrap(n.Identity, n.PeerHost, n.Routing, cfg) |
| 243 | return err |
| 244 | } |
| 245 | |
| 246 | var TempBootstrapPeersKey = datastore.NewKey("/local/temp_bootstrap_peers") |
| 247 | |
| 248 | func (n *IpfsNode) loadBootstrapPeers() ([]peer.AddrInfo, error) { |
| 249 | cfg, err := n.Repo.Config() |
| 250 | if err != nil { |
| 251 | return nil, err |
| 252 | } |
| 253 | |
| 254 | // Use auto-config resolution for actual bootstrap connectivity |
| 255 | return cfg.BootstrapPeersWithAutoConf() |
| 256 | } |
| 257 | |
| 258 | func (n *IpfsNode) saveTempBootstrapPeers(ctx context.Context, peerList []peer.AddrInfo) error { |
| 259 | ds := n.Repo.Datastore() |
| 260 | bytes, err := json.Marshal(config.BootstrapPeerStrings(peerList)) |
| 261 | if err != nil { |
| 262 | return err |
| 263 | } |
| 264 | |
| 265 | if err := ds.Put(ctx, TempBootstrapPeersKey, bytes); err != nil { |
| 266 | return err |
| 267 | } |
| 268 | return ds.Sync(ctx, TempBootstrapPeersKey) |
| 269 | } |
| 270 | |
| 271 | func (n *IpfsNode) loadTempBootstrapPeers(ctx context.Context) ([]peer.AddrInfo, error) { |
| 272 | ds := n.Repo.Datastore() |
| 273 | bytes, err := ds.Get(ctx, TempBootstrapPeersKey) |
| 274 | if err != nil { |
| 275 | return nil, err |
| 276 | } |
| 277 | |
| 278 | var addrs []string |
| 279 | if err := json.Unmarshal(bytes, &addrs); err != nil { |
| 280 | return nil, err |
| 281 | } |
| 282 | return config.ParseBootstrapPeers(addrs) |
| 283 | } |
| 284 | |
| 285 | type ConstructPeerHostOpts struct { |
| 286 | AddrsFactory p2pbhost.AddrsFactory |
| 287 | DisableNatPortMap bool |
| 288 | DisableRelay bool |
| 289 | EnableRelayHop bool |
| 290 | ConnectionManager connmgr.ConnManager |
| 291 | } |