mirror of
https://github.com/ipfs/kubo.git
synced 2025-08-06 19:44:01 +08:00

* plumb through go-datastore context changes * update go-libp2p to v0.16.0 * use LIBP2P_TCP_REUSEPORT instead of IPFS_REUSEPORT * use relay config * making deprecation notice match the go-ipfs-config key * docs(config): circuit relay v2 * docs(config): fix links and headers * feat(config): Internal.Libp2pForceReachability This switches to config that supports setting and reading Internal.Libp2pForceReachability OptionalString flag * use configuration option for static relays * chore: go-ipfs-config v0.18.0 https://github.com/ipfs/go-ipfs-config/releases/tag/v0.18.0 * feat: circuit v1 migration prompt when Swarm.EnableRelayHop is set (#8559) * exit when Swarm.EnableRelayHop is set * docs: Experimental.ShardingEnabled migration This ensures existing users of global sharding experiment get notified that the flag no longer works + that autosharding happens automatically. For people who NEED to keep the old behavior (eg. have no time to migrate today) there is a note about restoring it with `UnixFSShardingSizeThreshold`. * chore: add dag-jose code to the cid command output * add support for setting automatic unixfs sharding threshold from the config * test: have tests use low cutoff for sharding to mimic old behavior * test: change error message to match the current error * test: Add automatic sharding/unsharding tests (#8547) * test: refactored naming in the sharding sharness tests to make more sense * ci: set interop test executor to convenience image for Go1.16 + Node * ci: use interop master Co-authored-by: Marcin Rataj <lidel@lidel.org> Co-authored-by: Marten Seemann <martenseemann@gmail.com> Co-authored-by: Marcin Rataj <lidel@lidel.org> Co-authored-by: Gus Eggert <gus@gus.dev> Co-authored-by: Lucas Molas <schomatis@gmail.com>
367 lines
11 KiB
Go
367 lines
11 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
|
|
config "github.com/ipfs/go-ipfs-config"
|
|
files "github.com/ipfs/go-ipfs-files"
|
|
icore "github.com/ipfs/interface-go-ipfs-core"
|
|
icorepath "github.com/ipfs/interface-go-ipfs-core/path"
|
|
ma "github.com/multiformats/go-multiaddr"
|
|
|
|
"github.com/ipfs/go-ipfs/core"
|
|
"github.com/ipfs/go-ipfs/core/coreapi"
|
|
"github.com/ipfs/go-ipfs/core/node/libp2p"
|
|
"github.com/ipfs/go-ipfs/plugin/loader" // This package is needed so that all the preloaded plugins are loaded automatically
|
|
"github.com/ipfs/go-ipfs/repo/fsrepo"
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
)
|
|
|
|
/// ------ Setting up the IPFS Repo
|
|
|
|
func setupPlugins(externalPluginsPath string) error {
|
|
// Load any external plugins if available on externalPluginsPath
|
|
plugins, err := loader.NewPluginLoader(filepath.Join(externalPluginsPath, "plugins"))
|
|
if err != nil {
|
|
return fmt.Errorf("error loading plugins: %s", err)
|
|
}
|
|
|
|
// Load preloaded and external plugins
|
|
if err := plugins.Initialize(); err != nil {
|
|
return fmt.Errorf("error initializing plugins: %s", err)
|
|
}
|
|
|
|
if err := plugins.Inject(); err != nil {
|
|
return fmt.Errorf("error initializing plugins: %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func createTempRepo() (string, error) {
|
|
repoPath, err := ioutil.TempDir("", "ipfs-shell")
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get temp dir: %s", err)
|
|
}
|
|
|
|
// Create a config with default options and a 2048 bit key
|
|
cfg, err := config.Init(ioutil.Discard, 2048)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// When creating the repository, you can define custom settings on the repository, such as enabling experimental
|
|
// features (See experimental-features.md) or customizing the gateway endpoint.
|
|
// To do such things, you should modify the variable `cfg`. For example:
|
|
if *flagExp {
|
|
// https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md#ipfs-filestore
|
|
cfg.Experimental.FilestoreEnabled = true
|
|
// https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md#ipfs-urlstore
|
|
cfg.Experimental.UrlstoreEnabled = true
|
|
// https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md#ipfs-p2p
|
|
cfg.Experimental.Libp2pStreamMounting = true
|
|
// https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md#p2p-http-proxy
|
|
cfg.Experimental.P2pHttpProxy = true
|
|
// https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md#strategic-providing
|
|
cfg.Experimental.StrategicProviding = true
|
|
}
|
|
|
|
// Create the repo with the config
|
|
err = fsrepo.Init(repoPath, cfg)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to init ephemeral node: %s", err)
|
|
}
|
|
|
|
return repoPath, nil
|
|
}
|
|
|
|
/// ------ Spawning the node
|
|
|
|
// Creates an IPFS node and returns its coreAPI
|
|
func createNode(ctx context.Context, repoPath string) (icore.CoreAPI, error) {
|
|
// Open the repo
|
|
repo, err := fsrepo.Open(repoPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Construct the node
|
|
|
|
nodeOptions := &core.BuildCfg{
|
|
Online: true,
|
|
Routing: libp2p.DHTOption, // This option sets the node to be a full DHT node (both fetching and storing DHT Records)
|
|
// Routing: libp2p.DHTClientOption, // This option sets the node to be a client DHT node (only fetching records)
|
|
Repo: repo,
|
|
}
|
|
|
|
node, err := core.NewNode(ctx, nodeOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Attach the Core API to the constructed node
|
|
return coreapi.NewCoreAPI(node)
|
|
}
|
|
|
|
// Spawns a node on the default repo location, if the repo exists
|
|
func spawnDefault(ctx context.Context) (icore.CoreAPI, error) {
|
|
defaultPath, err := config.PathRoot()
|
|
if err != nil {
|
|
// shouldn't be possible
|
|
return nil, err
|
|
}
|
|
|
|
if err := setupPlugins(defaultPath); err != nil {
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return createNode(ctx, defaultPath)
|
|
}
|
|
|
|
// Spawns a node to be used just for this run (i.e. creates a tmp repo)
|
|
func spawnEphemeral(ctx context.Context) (icore.CoreAPI, error) {
|
|
if err := setupPlugins(""); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Create a Temporary Repo
|
|
repoPath, err := createTempRepo()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create temp repo: %s", err)
|
|
}
|
|
|
|
// Spawning an ephemeral IPFS node
|
|
return createNode(ctx, repoPath)
|
|
}
|
|
|
|
//
|
|
|
|
func connectToPeers(ctx context.Context, ipfs icore.CoreAPI, peers []string) error {
|
|
var wg sync.WaitGroup
|
|
peerInfos := make(map[peer.ID]*peer.AddrInfo, len(peers))
|
|
for _, addrStr := range peers {
|
|
addr, err := ma.NewMultiaddr(addrStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pii, err := peer.AddrInfoFromP2pAddr(addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pi, ok := peerInfos[pii.ID]
|
|
if !ok {
|
|
pi = &peer.AddrInfo{ID: pii.ID}
|
|
peerInfos[pi.ID] = pi
|
|
}
|
|
pi.Addrs = append(pi.Addrs, pii.Addrs...)
|
|
}
|
|
|
|
wg.Add(len(peerInfos))
|
|
for _, peerInfo := range peerInfos {
|
|
go func(peerInfo *peer.AddrInfo) {
|
|
defer wg.Done()
|
|
err := ipfs.Swarm().Connect(ctx, *peerInfo)
|
|
if err != nil {
|
|
log.Printf("failed to connect to %s: %s", peerInfo.ID, err)
|
|
}
|
|
}(peerInfo)
|
|
}
|
|
wg.Wait()
|
|
return nil
|
|
}
|
|
|
|
func getUnixfsFile(path string) (files.File, error) {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
st, err := file.Stat()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
f, err := files.NewReaderPathFile(path, file, st)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return f, nil
|
|
}
|
|
|
|
func getUnixfsNode(path string) (files.Node, error) {
|
|
st, err := os.Stat(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
f, err := files.NewSerialFile(path, false, st)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return f, nil
|
|
}
|
|
|
|
/// -------
|
|
|
|
var flagExp = flag.Bool("experimental", false, "enable experimental features")
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
/// --- Part I: Getting a IPFS node running
|
|
|
|
fmt.Println("-- Getting an IPFS node running -- ")
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
/*
|
|
// Spawn a node using the default path (~/.ipfs), assuming that a repo exists there already
|
|
fmt.Println("Spawning node on default repo")
|
|
ipfs, err := spawnDefault(ctx)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to spawnDefault node: %s", err))
|
|
}
|
|
*/
|
|
|
|
// Spawn a node using a temporary path, creating a temporary repo for the run
|
|
fmt.Println("Spawning node on a temporary repo")
|
|
ipfs, err := spawnEphemeral(ctx)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to spawn ephemeral node: %s", err))
|
|
}
|
|
|
|
fmt.Println("IPFS node is running")
|
|
|
|
/// --- Part II: Adding a file and a directory to IPFS
|
|
|
|
fmt.Println("\n-- Adding and getting back files & directories --")
|
|
|
|
inputBasePath := "../example-folder/"
|
|
inputPathFile := inputBasePath + "ipfs.paper.draft3.pdf"
|
|
inputPathDirectory := inputBasePath + "test-dir"
|
|
|
|
someFile, err := getUnixfsNode(inputPathFile)
|
|
if err != nil {
|
|
panic(fmt.Errorf("Could not get File: %s", err))
|
|
}
|
|
|
|
cidFile, err := ipfs.Unixfs().Add(ctx, someFile)
|
|
if err != nil {
|
|
panic(fmt.Errorf("Could not add File: %s", err))
|
|
}
|
|
|
|
fmt.Printf("Added file to IPFS with CID %s\n", cidFile.String())
|
|
|
|
someDirectory, err := getUnixfsNode(inputPathDirectory)
|
|
if err != nil {
|
|
panic(fmt.Errorf("Could not get File: %s", err))
|
|
}
|
|
|
|
cidDirectory, err := ipfs.Unixfs().Add(ctx, someDirectory)
|
|
if err != nil {
|
|
panic(fmt.Errorf("Could not add Directory: %s", err))
|
|
}
|
|
|
|
fmt.Printf("Added directory to IPFS with CID %s\n", cidDirectory.String())
|
|
|
|
/// --- Part III: Getting the file and directory you added back
|
|
|
|
outputBasePath, err := ioutil.TempDir("", "example")
|
|
if err != nil {
|
|
panic(fmt.Errorf("could not create output dir (%v)", err))
|
|
}
|
|
fmt.Printf("output folder: %s\n", outputBasePath)
|
|
outputPathFile := outputBasePath + strings.Split(cidFile.String(), "/")[2]
|
|
outputPathDirectory := outputBasePath + strings.Split(cidDirectory.String(), "/")[2]
|
|
|
|
rootNodeFile, err := ipfs.Unixfs().Get(ctx, cidFile)
|
|
if err != nil {
|
|
panic(fmt.Errorf("Could not get file with CID: %s", err))
|
|
}
|
|
|
|
err = files.WriteTo(rootNodeFile, outputPathFile)
|
|
if err != nil {
|
|
panic(fmt.Errorf("Could not write out the fetched CID: %s", err))
|
|
}
|
|
|
|
fmt.Printf("Got file back from IPFS (IPFS path: %s) and wrote it to %s\n", cidFile.String(), outputPathFile)
|
|
|
|
rootNodeDirectory, err := ipfs.Unixfs().Get(ctx, cidDirectory)
|
|
if err != nil {
|
|
panic(fmt.Errorf("Could not get file with CID: %s", err))
|
|
}
|
|
|
|
err = files.WriteTo(rootNodeDirectory, outputPathDirectory)
|
|
if err != nil {
|
|
panic(fmt.Errorf("Could not write out the fetched CID: %s", err))
|
|
}
|
|
|
|
fmt.Printf("Got directory back from IPFS (IPFS path: %s) and wrote it to %s\n", cidDirectory.String(), outputPathDirectory)
|
|
|
|
/// --- Part IV: Getting a file from the IPFS Network
|
|
|
|
fmt.Println("\n-- Going to connect to a few nodes in the Network as bootstrappers --")
|
|
|
|
bootstrapNodes := []string{
|
|
// IPFS Bootstrapper nodes.
|
|
"/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
|
|
"/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
|
|
"/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
|
|
"/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt",
|
|
|
|
// IPFS Cluster Pinning nodes
|
|
"/ip4/138.201.67.219/tcp/4001/p2p/QmUd6zHcbkbcs7SMxwLs48qZVX3vpcM8errYS7xEczwRMA",
|
|
"/ip4/138.201.67.219/udp/4001/quic/p2p/QmUd6zHcbkbcs7SMxwLs48qZVX3vpcM8errYS7xEczwRMA",
|
|
"/ip4/138.201.67.220/tcp/4001/p2p/QmNSYxZAiJHeLdkBg38roksAR9So7Y5eojks1yjEcUtZ7i",
|
|
"/ip4/138.201.67.220/udp/4001/quic/p2p/QmNSYxZAiJHeLdkBg38roksAR9So7Y5eojks1yjEcUtZ7i",
|
|
"/ip4/138.201.68.74/tcp/4001/p2p/QmdnXwLrC8p1ueiq2Qya8joNvk3TVVDAut7PrikmZwubtR",
|
|
"/ip4/138.201.68.74/udp/4001/quic/p2p/QmdnXwLrC8p1ueiq2Qya8joNvk3TVVDAut7PrikmZwubtR",
|
|
"/ip4/94.130.135.167/tcp/4001/p2p/QmUEMvxS2e7iDrereVYc5SWPauXPyNwxcy9BXZrC1QTcHE",
|
|
"/ip4/94.130.135.167/udp/4001/quic/p2p/QmUEMvxS2e7iDrereVYc5SWPauXPyNwxcy9BXZrC1QTcHE",
|
|
|
|
// You can add more nodes here, for example, another IPFS node you might have running locally, mine was:
|
|
// "/ip4/127.0.0.1/tcp/4010/p2p/QmZp2fhDLxjYue2RiUvLwT9MWdnbDxam32qYFnGmxZDh5L",
|
|
// "/ip4/127.0.0.1/udp/4010/quic/p2p/QmZp2fhDLxjYue2RiUvLwT9MWdnbDxam32qYFnGmxZDh5L",
|
|
}
|
|
|
|
go func() {
|
|
err := connectToPeers(ctx, ipfs, bootstrapNodes)
|
|
if err != nil {
|
|
log.Printf("failed connect to peers: %s", err)
|
|
}
|
|
}()
|
|
|
|
exampleCIDStr := "QmUaoioqU7bxezBQZkUcgcSyokatMY71sxsALxQmRRrHrj"
|
|
|
|
fmt.Printf("Fetching a file from the network with CID %s\n", exampleCIDStr)
|
|
outputPath := outputBasePath + exampleCIDStr
|
|
testCID := icorepath.New(exampleCIDStr)
|
|
|
|
rootNode, err := ipfs.Unixfs().Get(ctx, testCID)
|
|
if err != nil {
|
|
panic(fmt.Errorf("Could not get file with CID: %s", err))
|
|
}
|
|
|
|
err = files.WriteTo(rootNode, outputPath)
|
|
if err != nil {
|
|
panic(fmt.Errorf("Could not write out the fetched CID: %s", err))
|
|
}
|
|
|
|
fmt.Printf("Wrote the file to %s\n", outputPath)
|
|
|
|
fmt.Println("\nAll done! You just finalized your first tutorial on how to use go-ipfs as a library")
|
|
}
|