builder.go 4.06 KB
Newer Older
Jeromy's avatar
Jeromy committed
1 2 3
package core

import (
4 5
	"crypto/rand"
	"encoding/base64"
Jeromy's avatar
Jeromy committed
6 7
	"errors"

8
	bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
9
	key "github.com/ipfs/go-ipfs/blocks/key"
10 11 12 13 14
	bserv "github.com/ipfs/go-ipfs/blockservice"
	offline "github.com/ipfs/go-ipfs/exchange/offline"
	dag "github.com/ipfs/go-ipfs/merkledag"
	path "github.com/ipfs/go-ipfs/path"
	pin "github.com/ipfs/go-ipfs/pin"
15
	repo "github.com/ipfs/go-ipfs/repo"
16
	cfg "github.com/ipfs/go-ipfs/repo/config"
Jeromy's avatar
Jeromy committed
17 18
	ds "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore"
	dsync "gx/ipfs/QmfQzVugPq1w5shWRcLWSeiHF4a2meBX7yVD8Vw7GWJM9o/go-datastore/sync"
Jeromy's avatar
Jeromy committed
19

20
	pstore "gx/ipfs/QmQdnfvZQuhdT93LNc5bos52wAmdr3G2p6G8teLJMEN32P/go-libp2p-peerstore"
21
	goprocessctx "gx/ipfs/QmQopLATEYMNg7dVqZRNDfeE2S1yKy8zrRh5xnYiuqeZBn/goprocess/context"
22
	ci "gx/ipfs/QmUWER4r4qMvaCnX5zREcfyiWN7cXN9g3a7fkRqNz8qWPP/go-libp2p-crypto"
Jeromy's avatar
Jeromy committed
23
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
Jeromy's avatar
Jeromy committed
24 25
)

26 27 28
type BuildCfg struct {
	// If online is set, the node will have networking enabled
	Online bool
Jeromy's avatar
Jeromy committed
29

30 31 32 33 34 35
	// If NilRepo is set, a repo backed by a nil datastore will be constructed
	NilRepo bool

	Routing RoutingOption
	Host    HostOption
	Repo    repo.Repo
Jeromy's avatar
Jeromy committed
36 37
}

38 39 40
func (cfg *BuildCfg) fillDefaults() error {
	if cfg.Repo != nil && cfg.NilRepo {
		return errors.New("cannot set a repo and specify nilrepo at the same time")
Jeromy's avatar
Jeromy committed
41
	}
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

	if cfg.Repo == nil {
		var d ds.Datastore
		d = ds.NewMapDatastore()
		if cfg.NilRepo {
			d = ds.NewNullDatastore()
		}
		r, err := defaultRepo(dsync.MutexWrap(d))
		if err != nil {
			return err
		}
		cfg.Repo = r
	}

	if cfg.Routing == nil {
		cfg.Routing = DHTOption
	}

	if cfg.Host == nil {
		cfg.Host = DefaultHostOption
	}

	return nil
Jeromy's avatar
Jeromy committed
65 66
}

67
func defaultRepo(dstore repo.Datastore) (repo.Repo, error) {
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
	c := cfg.Config{}
	priv, pub, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, rand.Reader)
	if err != nil {
		return nil, err
	}

	data, err := pub.Hash()
	if err != nil {
		return nil, err
	}

	privkeyb, err := priv.Bytes()
	if err != nil {
		return nil, err
	}

	c.Bootstrap = cfg.DefaultBootstrapAddresses
	c.Addresses.Swarm = []string{"/ip4/0.0.0.0/tcp/4001"}
	c.Identity.PeerID = key.Key(data).B58String()
	c.Identity.PrivKey = base64.StdEncoding.EncodeToString(privkeyb)

Jeromy's avatar
Jeromy committed
89
	return &repo.Mock{
Jeromy's avatar
Jeromy committed
90
		D: dstore,
91 92
		C: c,
	}, nil
Jeromy's avatar
Jeromy committed
93 94
}

95 96 97 98
func NewNode(ctx context.Context, cfg *BuildCfg) (*IpfsNode, error) {
	if cfg == nil {
		cfg = new(BuildCfg)
	}
Jeromy's avatar
Jeromy committed
99

100 101 102 103
	err := cfg.fillDefaults()
	if err != nil {
		return nil, err
	}
Jeromy's avatar
Jeromy committed
104

105 106 107 108
	n := &IpfsNode{
		mode:      offlineMode,
		Repo:      cfg.Repo,
		ctx:       ctx,
Jeromy's avatar
Jeromy committed
109
		Peerstore: pstore.NewPeerstore(),
110 111 112 113
	}
	if cfg.Online {
		n.mode = onlineMode
	}
Jeromy's avatar
Jeromy committed
114

115 116
	// TODO: this is a weird circular-ish dependency, rework it
	n.proc = goprocessctx.WithContextAndTeardown(ctx, n.teardown)
Jeromy's avatar
Jeromy committed
117

118 119 120 121
	if err := setupNode(ctx, n, cfg); err != nil {
		n.Close()
		return nil, err
	}
Jeromy's avatar
Jeromy committed
122

123
	return n, nil
Jeromy's avatar
Jeromy committed
124 125
}

126 127 128 129
func setupNode(ctx context.Context, n *IpfsNode, cfg *BuildCfg) error {
	// setup local peer ID (private key is loaded in online setup)
	if err := n.loadID(); err != nil {
		return err
Jeromy's avatar
Jeromy committed
130
	}
131 132

	var err error
133
	bs := bstore.NewBlockstore(n.Repo.Datastore())
134
	n.Blockstore, err = bstore.BloomCached(bs, 256*1024, kSizeBlockstoreWriteCache)
135 136 137 138
	if err != nil {
		return err
	}

139 140 141 142 143 144 145 146 147
	rcfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	if rcfg.Datastore.HashOnRead {
		bs.RuntimeHashing(true)
	}

148
	if cfg.Online {
149
		do := setupDiscoveryOption(rcfg.Discovery)
150 151
		if err := n.startOnlineServices(ctx, cfg.Routing, cfg.Host, do); err != nil {
			return err
152
		}
153 154
	} else {
		n.Exchange = offline.Exchange(n.Blockstore)
Jeromy's avatar
Jeromy committed
155
	}
156 157 158 159 160 161 162 163 164 165 166 167 168

	n.Blocks = bserv.New(n.Blockstore, n.Exchange)
	n.DAG = dag.NewDAGService(n.Blocks)
	n.Pinning, err = pin.LoadPinner(n.Repo.Datastore(), n.DAG)
	if err != nil {
		// TODO: we should move towards only running 'NewPinner' explicity on
		// node init instead of implicitly here as a result of the pinner keys
		// not being found in the datastore.
		// this is kinda sketchy and could cause data loss
		n.Pinning = pin.NewPinner(n.Repo.Datastore(), n.DAG)
	}
	n.Resolver = &path.Resolver{DAG: n.DAG}

Jeromy's avatar
Jeromy committed
169 170 171 172 173
	err = n.loadFilesRoot()
	if err != nil {
		return err
	}

174
	return nil
Jeromy's avatar
Jeromy committed
175
}